<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>http://wiki.openrocket.info/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=JoePfeiffer</id>
	<title>OpenRocket wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.openrocket.info/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=JoePfeiffer"/>
	<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/Special:Contributions/JoePfeiffer"/>
	<updated>2026-04-07T12:38:00Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.8</generator>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35958</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35958"/>
		<updated>2023-09-11T16:53:39Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: fix path to AbstractSimulationListener.java&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/listeners/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt; (so it isn&#039;t necessary to call a constructor yourself). &amp;lt;code&amp;gt;core/src/net/sf/openrocket/util/Config.java&amp;lt;/code&amp;gt; includes methods to interact with a configurator, allowing the extension to obtain &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt;, and other configuration values.&lt;br /&gt;
&lt;br /&gt;
In this case, we&#039;ll only be defining a single configuration field in our configurator, &amp;lt;code&amp;gt;&amp;quot;launchAltitude&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; method obtains the air-start altitude for the simulation from the configuration, and sets a default air-start altitude of 1000 meters. Our &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method has been modified to make use of this to obtain the user&#039;s requested air-start altitude from the configurator, in place of the original hard-coded value.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; method places a new launch altitude in the configuration.  While our extension doesn&#039;t make use of this method directly, it will be necessary for our configurator. The call to &amp;lt;code&amp;gt;fireChangeEvent()&amp;lt;/code&amp;gt; in this method assures that the changes we make to the air-start altitude are propagated throughout the simulation.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most important things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier. The name of the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;, &#039;&#039;&#039;&#039;&#039;MUST&#039;&#039;&#039;&#039;&#039; match the names of the corresponding &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; methods exactly.  If they don&#039;t, there will be an exception at run time when the user attempts to change the value.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are provided by the system.&lt;br /&gt;
====Example User Extensions Provided With OpenRocket====&lt;br /&gt;
Several examples of user extensions are provided in the OpenRocket source tree.  As mentioned previously, the extensions are all located in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt; and their configurators are all located in &amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt;.  Also recall that every extension has a corresponding provider.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
!Purpose!!Extension!!Configurator&lt;br /&gt;
|-&lt;br /&gt;
| Set air-start altitude and velocity||&amp;lt;code&amp;gt;AirStart.java&amp;lt;/code&amp;gt;||&amp;lt;code&amp;gt;AirStartConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Save some simulation values as a CSV file||&amp;lt;code&amp;gt;CSVSave.java&amp;lt;/code&amp;gt;||&#039;&#039;(none)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Calculate damping moment coefficient after every simulation step||&amp;lt;code&amp;gt;DampingMoment.java&amp;lt;/code&amp;gt;||&#039;&#039;(none)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Print summary of simulation progress after each step||&amp;lt;code&amp;gt;PrintSimulation.java&amp;lt;/code&amp;gt;||&#039;&#039;(none)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Active roll control||&amp;lt;code&amp;gt;RollControl.java&amp;lt;/code&amp;gt;||&amp;lt;code&amp;gt;RollControlConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Stop simulation at specified time or number of steps||&amp;lt;code&amp;gt;StopSimulation&amp;lt;/code&amp;gt;||&amp;lt;code&amp;gt;StopSimulationConfigurator&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&#039;&#039;Note:  documentation for adding user-created simulation listeners, without making use of the full extension mechanism, is also available at [[Simulation Listeners]]&#039;&#039;&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Main_Page&amp;diff=35818</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Main_Page&amp;diff=35818"/>
		<updated>2023-01-26T19:48:48Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is the wiki for OpenRocket, an open-source model rocket simulator.  The OpenRocket documentation is written collaboratively here.&lt;br /&gt;
&lt;br /&gt;
The main web site for OpenRocket is &#039;&#039;&#039;http://openrocket.info/&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;Donations for OpenRocket Development&amp;lt;/b&amp;gt; =&lt;br /&gt;
&lt;br /&gt;
Through OpenCollective, you can now &#039;&#039;&#039;&#039;&#039;become a sponsor&#039;&#039;&#039;&#039;&#039; for the development of OpenRocket. The OpenRocket project is now officially accepting donations to fund &#039;&#039;&#039;&#039;&#039;out-of-pocket&#039;&#039; hard costs&#039;&#039;&#039;.  We don’t need much, but we will be incurring expenses going forward for code signing, which should eliminate the need to disable security when downloading, installing, and running OpenRocket. For full details, head over to &#039;&#039;&#039;[https://opencollective.com/openrocket#category-CONTRIBUTE &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Contribute at OpenCollective--OpenRocket&amp;lt;/b&amp;gt;]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= OpenRocket User&#039;s Guide =&lt;br /&gt;
&lt;br /&gt;
1. [[Introduction]]&lt;br /&gt;
&lt;br /&gt;
2. [[Downloading &amp;amp; Installing]]&lt;br /&gt;
&lt;br /&gt;
3. [[Getting Started]]&lt;br /&gt;
&lt;br /&gt;
4. [[Basic Rocket Design]]&lt;br /&gt;
&lt;br /&gt;
5. [[Basic Flight Simulation]] &lt;br /&gt;
&lt;br /&gt;
6. [[Advanced Rocket Design]]&lt;br /&gt;
&lt;br /&gt;
7. [[Advanced Flight Simulation]]&lt;br /&gt;
&lt;br /&gt;
8. [[Overrides and Surface Finish]]&lt;br /&gt;
&lt;br /&gt;
9. [[Rocket Analysis]]&lt;br /&gt;
&lt;br /&gt;
10. [[Custom Expressions]]&lt;br /&gt;
&lt;br /&gt;
11. [[Simulation Extensions]]&lt;br /&gt;
&lt;br /&gt;
== Appendices: ==&lt;br /&gt;
&lt;br /&gt;
A. [[FAQ|Frequently Asked Questions]]&lt;br /&gt;
&lt;br /&gt;
B. [[Component Details]]&lt;br /&gt;
&lt;br /&gt;
C. [[Tips|Tips and Tricks]]&lt;br /&gt;
&lt;br /&gt;
D. [[List of Useful Custom Expressions]]&lt;br /&gt;
&lt;br /&gt;
E. [[Scripting with Python and JPype]]&lt;br /&gt;
&lt;br /&gt;
F. [[Resources]]&lt;br /&gt;
&lt;br /&gt;
G. [[Feature Comparison]]&lt;br /&gt;
&lt;br /&gt;
H. [[Third-Party Compatibility]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;[[Instructions for Translators]]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Developer resources =&lt;br /&gt;
&lt;br /&gt;
The [https://github.com/openrocket/openrocket/wiki OpenRocket Developer Wiki] is on the development site at [https://github.com/openrocket/openrocket/ GitHub].&lt;br /&gt;
&lt;br /&gt;
The developer pages were unified and migrated there in March, 2021. If you&#039;re missing a developer page, please check on the developer wiki.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Main_Page&amp;diff=35817</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Main_Page&amp;diff=35817"/>
		<updated>2023-01-26T19:46:55Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is the wiki for OpenRocket, an open-source model rocket simulator.  The OpenRocket documentation is written collaboratively here.&lt;br /&gt;
&lt;br /&gt;
The main web site for OpenRocket is &#039;&#039;&#039;http://openrocket.info/&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;Donations for OpenRocket Development&amp;lt;/b&amp;gt; =&lt;br /&gt;
&lt;br /&gt;
Through OpenCollective, you can now &#039;&#039;&#039;&#039;&#039;become a sponsor&#039;&#039;&#039;&#039;&#039; for the development of OpenRocket. The OpenRocket project is now officially accepting donations to fund &#039;&#039;&#039;&#039;&#039;out-of-pocket&#039;&#039; hard costs&#039;&#039;&#039;.  We don’t need much, but we will be incurring expenses going forward for code signing, which should eliminate the need to disable security when downloading, installing, and running OpenRocket. For full details, head over to &#039;&#039;&#039;[https://opencollective.com/openrocket#category-CONTRIBUTE &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Contribute at OpenCollective--OpenRocket&amp;lt;/b&amp;gt;]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= OpenRocket User&#039;s Guide =&lt;br /&gt;
&lt;br /&gt;
# [[Introduction]]&lt;br /&gt;
&lt;br /&gt;
# [[Downloading &amp;amp; Installing]]&lt;br /&gt;
&lt;br /&gt;
# [[Getting Started]]&lt;br /&gt;
&lt;br /&gt;
# [[Basic Rocket Design]]&lt;br /&gt;
&lt;br /&gt;
# [[Basic Flight Simulation]] &lt;br /&gt;
&lt;br /&gt;
# [[Advanced Rocket Design]]&lt;br /&gt;
&lt;br /&gt;
# [[Advanced Flight Simulation]]&lt;br /&gt;
&lt;br /&gt;
# [[Overrides and Surface Finish]]&lt;br /&gt;
&lt;br /&gt;
# [[Rocket Analysis]]&lt;br /&gt;
&lt;br /&gt;
# [[Custom Expressions]]&lt;br /&gt;
&lt;br /&gt;
# [[Simulation Extensions]]&lt;br /&gt;
&lt;br /&gt;
== Appendices: ==&lt;br /&gt;
&lt;br /&gt;
A. [[FAQ|Frequently Asked Questions]]&lt;br /&gt;
&lt;br /&gt;
B. [[Component Details]]&lt;br /&gt;
&lt;br /&gt;
C. [[Tips|Tips and Tricks]]&lt;br /&gt;
&lt;br /&gt;
D. [[List of Useful Custom Expressions]]&lt;br /&gt;
&lt;br /&gt;
E. [[Scripting with Python and JPype]]&lt;br /&gt;
&lt;br /&gt;
F. [[Resources]]&lt;br /&gt;
&lt;br /&gt;
G. [[Feature Comparison]]&lt;br /&gt;
&lt;br /&gt;
H. [[Third-Party Compatibility]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;[[Instructions for Translators]]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Developer resources =&lt;br /&gt;
&lt;br /&gt;
The [https://github.com/openrocket/openrocket/wiki OpenRocket Developer Wiki] is on the development site at [https://github.com/openrocket/openrocket/ GitHub].&lt;br /&gt;
&lt;br /&gt;
The developer pages were unified and migrated there in March, 2021. If you&#039;re missing a developer page, please check on the developer wiki.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=User:JoePfeiffer/Extensions_and_Listeners&amp;diff=35815</id>
		<title>User:JoePfeiffer/Extensions and Listeners</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=User:JoePfeiffer/Extensions_and_Listeners&amp;diff=35815"/>
		<updated>2023-01-26T19:42:18Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: JoePfeiffer moved page User:JoePfeiffer/Extensions and Listeners to Simulation Extensions: Go public&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Simulation Extensions]]&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35814</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35814"/>
		<updated>2023-01-26T19:42:17Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: JoePfeiffer moved page User:JoePfeiffer/Extensions and Listeners to Simulation Extensions: Go public&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt; (so it isn&#039;t necessary to call a constructor yourself). &amp;lt;code&amp;gt;core/src/net/sf/openrocket/util/Config.java&amp;lt;/code&amp;gt; includes methods to interact with a configurator, allowing the extension to obtain &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt;, and other configuration values.&lt;br /&gt;
&lt;br /&gt;
In this case, we&#039;ll only be defining a single configuration field in our configurator, &amp;lt;code&amp;gt;&amp;quot;launchAltitude&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; method obtains the air-start altitude for the simulation from the configuration, and sets a default air-start altitude of 1000 meters. Our &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method has been modified to make use of this to obtain the user&#039;s requested air-start altitude from the configurator, in place of the original hard-coded value.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; method places a new launch altitude in the configuration.  While our extension doesn&#039;t make use of this method directly, it will be necessary for our configurator. The call to &amp;lt;code&amp;gt;fireChangeEvent()&amp;lt;/code&amp;gt; in this method assures that the changes we make to the air-start altitude are propagated throughout the simulation.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most important things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier. The name of the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;, &#039;&#039;&#039;&#039;&#039;MUST&#039;&#039;&#039;&#039;&#039; match the names of the corresponding &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; methods exactly.  If they don&#039;t, there will be an exception at run time when the user attempts to change the value.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are provided by the system.&lt;br /&gt;
====Example User Extensions Provided With OpenRocket====&lt;br /&gt;
Several examples of user extensions are provided in the OpenRocket source tree.  As mentioned previously, the extensions are all located in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt; and their configurators are all located in &amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt;.  Also recall that every extension has a corresponding provider.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
!Purpose!!Extension!!Configurator&lt;br /&gt;
|-&lt;br /&gt;
| Set air-start altitude and velocity||&amp;lt;code&amp;gt;AirStart.java&amp;lt;/code&amp;gt;||&amp;lt;code&amp;gt;AirStartConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Save some simulation values as a CSV file||&amp;lt;code&amp;gt;CSVSave.java&amp;lt;/code&amp;gt;||&#039;&#039;(none)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Calculate damping moment coefficient after every simulation step||&amp;lt;code&amp;gt;DampingMoment.java&amp;lt;/code&amp;gt;||&#039;&#039;(none)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Print summary of simulation progress after each step||&amp;lt;code&amp;gt;PrintSimulation.java&amp;lt;/code&amp;gt;||&#039;&#039;(none)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Active roll control||&amp;lt;code&amp;gt;RollControl.java&amp;lt;/code&amp;gt;||&amp;lt;code&amp;gt;RollControlConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Stop simulation at specified time or number of steps||&amp;lt;code&amp;gt;StopSimulation&amp;lt;/code&amp;gt;||&amp;lt;code&amp;gt;StopSimulationConfigurator&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&#039;&#039;Note:  documentation for adding user-created simulation listeners, without making use of the full extension mechanism, is also available at [[Simulation Listeners]]&#039;&#039;&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35813</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35813"/>
		<updated>2023-01-26T19:41:24Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt; (so it isn&#039;t necessary to call a constructor yourself). &amp;lt;code&amp;gt;core/src/net/sf/openrocket/util/Config.java&amp;lt;/code&amp;gt; includes methods to interact with a configurator, allowing the extension to obtain &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt;, and other configuration values.&lt;br /&gt;
&lt;br /&gt;
In this case, we&#039;ll only be defining a single configuration field in our configurator, &amp;lt;code&amp;gt;&amp;quot;launchAltitude&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; method obtains the air-start altitude for the simulation from the configuration, and sets a default air-start altitude of 1000 meters. Our &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method has been modified to make use of this to obtain the user&#039;s requested air-start altitude from the configurator, in place of the original hard-coded value.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; method places a new launch altitude in the configuration.  While our extension doesn&#039;t make use of this method directly, it will be necessary for our configurator. The call to &amp;lt;code&amp;gt;fireChangeEvent()&amp;lt;/code&amp;gt; in this method assures that the changes we make to the air-start altitude are propagated throughout the simulation.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most important things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier. The name of the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;, &#039;&#039;&#039;&#039;&#039;MUST&#039;&#039;&#039;&#039;&#039; match the names of the corresponding &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; methods exactly.  If they don&#039;t, there will be an exception at run time when the user attempts to change the value.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are provided by the system.&lt;br /&gt;
====Example User Extensions Provided With OpenRocket====&lt;br /&gt;
Several examples of user extensions are provided in the OpenRocket source tree.  As mentioned previously, the extensions are all located in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt; and their configurators are all located in &amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt;.  Also recall that every extension has a corresponding provider.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
!Purpose!!Extension!!Configurator&lt;br /&gt;
|-&lt;br /&gt;
| Set air-start altitude and velocity||&amp;lt;code&amp;gt;AirStart.java&amp;lt;/code&amp;gt;||&amp;lt;code&amp;gt;AirStartConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Save some simulation values as a CSV file||&amp;lt;code&amp;gt;CSVSave.java&amp;lt;/code&amp;gt;||&#039;&#039;(none)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Calculate damping moment coefficient after every simulation step||&amp;lt;code&amp;gt;DampingMoment.java&amp;lt;/code&amp;gt;||&#039;&#039;(none)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Print summary of simulation progress after each step||&amp;lt;code&amp;gt;PrintSimulation.java&amp;lt;/code&amp;gt;||&#039;&#039;(none)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Active roll control||&amp;lt;code&amp;gt;RollControl.java&amp;lt;/code&amp;gt;||&amp;lt;code&amp;gt;RollControlConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Stop simulation at specified time or number of steps||&amp;lt;code&amp;gt;StopSimulation&amp;lt;/code&amp;gt;||&amp;lt;code&amp;gt;StopSimulationConfigurator&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&#039;&#039;Note:  documentation for adding user-created simulation listeners, without making use of the full extension mechanism, is also available at [[Simulation Listeners]]&#039;&#039;&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35812</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35812"/>
		<updated>2023-01-26T19:40:21Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt; (so it isn&#039;t necessary to call a constructor yourself). &amp;lt;code&amp;gt;core/src/net/sf/openrocket/util/Config.java&amp;lt;/code&amp;gt; includes methods to interact with a configurator, allowing the extension to obtain &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt;, and other configuration values.&lt;br /&gt;
&lt;br /&gt;
In this case, we&#039;ll only be defining a single configuration field in our configurator, &amp;lt;code&amp;gt;&amp;quot;launchAltitude&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; method obtains the air-start altitude for the simulation from the configuration, and sets a default air-start altitude of 1000 meters. Our &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method has been modified to make use of this to obtain the user&#039;s requested air-start altitude from the configurator, in place of the original hard-coded value.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; method places a new launch altitude in the configuration.  While our extension doesn&#039;t make use of this method directly, it will be necessary for our configurator. The call to &amp;lt;code&amp;gt;fireChangeEvent()&amp;lt;/code&amp;gt; in this method assures that the changes we make to the air-start altitude are propagated throughout the simulation.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most important things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier. The name of the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;, &#039;&#039;&#039;&#039;&#039;MUST&#039;&#039;&#039;&#039;&#039; match the names of the corresponding &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; methods exactly.  If they don&#039;t, there will be an exception at run time when the user attempts to change the value.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are provided by the system.&lt;br /&gt;
====Example User Extensions Provided With OpenRocket====&lt;br /&gt;
Several examples of user extensions are provided in the OpenRocket source tree.  As mentioned previously, the extensions are all located in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt; and their configurators are all located in &amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt;.  Also recall that every extension has a corresponding provider.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
!Purpose!!Extension!!Configurator&lt;br /&gt;
|-&lt;br /&gt;
| Set air-start altitude and velocity||&amp;lt;code&amp;gt;AirStart.java&amp;lt;/code&amp;gt;||&amp;lt;code&amp;gt;AirStartConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Save some simulation values as a CSV file||&amp;lt;code&amp;gt;CSVSave.java&amp;lt;/code&amp;gt;||&#039;&#039;(none)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Calculate damping moment coefficient after every simulation step||&amp;lt;code&amp;gt;DampingMoment.java&amp;lt;/code&amp;gt;||&#039;&#039;(none)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Print summary of simulation progress after each step||&amp;lt;code&amp;gt;PrintSimulation.java&amp;lt;/code&amp;gt;||&#039;&#039;(none)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Active roll control||&amp;lt;code&amp;gt;RollControl.java&amp;lt;/code&amp;gt;||&amp;lt;code&amp;gt;RollControlConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Stop simulation at specified time or number of steps||&amp;lt;code&amp;gt;StopSimulation&amp;lt;/code&amp;gt;||&amp;lt;code&amp;gt;StopSimulationConfigurator&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&#039;&#039;Note:  documentation for adding user-created simulation listeners, without making use of the full extension mechanism, is also available at [[Simulation Listeners]]&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35811</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35811"/>
		<updated>2023-01-26T19:36:14Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt; (so it isn&#039;t necessary to call a constructor yourself). &amp;lt;code&amp;gt;core/src/net/sf/openrocket/util/Config.java&amp;lt;/code&amp;gt; includes methods to interact with a configurator, allowing the extension to obtain &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt;, and other configuration values.&lt;br /&gt;
&lt;br /&gt;
In this case, we&#039;ll only be defining a single configuration field in our configurator, &amp;lt;code&amp;gt;&amp;quot;launchAltitude&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; method obtains the air-start altitude for the simulation from the configuration, and sets a default air-start altitude of 1000 meters. Our &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method has been modified to make use of this to obtain the user&#039;s requested air-start altitude from the configurator, in place of the original hard-coded value.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; method places a new launch altitude in the configuration.  While our extension doesn&#039;t make use of this method directly, it will be necessary for our configurator. The call to &amp;lt;code&amp;gt;fireChangeEvent()&amp;lt;/code&amp;gt; in this method assures that the changes we make to the air-start altitude are propagated throughout the simulation.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most important things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier. The name of the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;, &#039;&#039;&#039;&#039;&#039;MUST&#039;&#039;&#039;&#039;&#039; match the names of the corresponding &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; methods exactly.  If they don&#039;t, there will be an exception at run time when the user attempts to change the value.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are provided by the system.&lt;br /&gt;
====Example User Extensions Provided With OpenRocket====&lt;br /&gt;
Several examples of user extensions are provided in the OpenRocket source tree.  As mentioned previously, the extensions are all located in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt; and their configurators are all located in &amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt;.  Also recall that every extension has a corresponding provider.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
!Purpose!!Extension!!Configurator&lt;br /&gt;
|-&lt;br /&gt;
| Set air-start altitude and velocity||&amp;lt;code&amp;gt;AirStart.java&amp;lt;/code&amp;gt;||&amp;lt;code&amp;gt;AirStartConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Save some simulation values as a CSV file||&amp;lt;code&amp;gt;CSVSave.java&amp;lt;/code&amp;gt;||&#039;&#039;(none)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Calculate damping moment coefficient after every simulation step||&amp;lt;code&amp;gt;DampingMoment.java&amp;lt;/code&amp;gt;||&#039;&#039;(none)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Print summary of simulation progress after each step||&amp;lt;code&amp;gt;PrintSimulation.java&amp;lt;/code&amp;gt;||&#039;&#039;(none)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Active roll control||&amp;lt;code&amp;gt;RollControl.java&amp;lt;/code&amp;gt;||&amp;lt;code&amp;gt;RollControlConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Stop simulation at specified time or number of steps||&amp;lt;code&amp;gt;StopSimulation&amp;lt;/code&amp;gt;||&amp;lt;code&amp;gt;StopSimulationConfigurator&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35810</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35810"/>
		<updated>2023-01-26T19:33:05Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt; (so it isn&#039;t necessary to call a constructor yourself). &amp;lt;code&amp;gt;core/src/net/sf/openrocket/util/Config.java&amp;lt;/code&amp;gt; includes methods to interact with a configurator, allowing the extension to obtain &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt;, and other configuration values.&lt;br /&gt;
&lt;br /&gt;
In this case, we&#039;ll only be defining a single configuration field in our configurator, &amp;lt;code&amp;gt;&amp;quot;launchAltitude&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; method obtains the air-start altitude for the simulation from the configuration, and sets a default air-start altitude of 1000 meters. Our &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method has been modified to make use of this to obtain the user&#039;s requested air-start altitude from the configurator, in place of the original hard-coded value.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; method places a new launch altitude in the configuration.  While our extension doesn&#039;t make use of this method directly, it will be necessary for our configurator. The call to &amp;lt;code&amp;gt;fireChangeEvent()&amp;lt;/code&amp;gt; in this method assures that the changes we make to the air-start altitude are propagated throughout the simulation.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most important things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier. The name of the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;, &#039;&#039;&#039;&#039;&#039;MUST&#039;&#039;&#039;&#039;&#039; match the names of the corresponding &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; methods exactly.  If they don&#039;t, there will be an exception at run time when the user attempts to change the value.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are provided by the system.&lt;br /&gt;
====Example User Extensions Provided With OpenRocket====&lt;br /&gt;
Several examples of user extensions are provided in the OpenRocket source tree.  As mentioned previously, the extensions are all located in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt; and their configurators are all located in &amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt;.  Also recall that every extension has a corresponding provider.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
! Header text !! Header text !! Header text&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
!Purpose!!Extension!!Configurator&lt;br /&gt;
|-&lt;br /&gt;
| Set air-start altitude and velocity&amp;lt;code&amp;gt;AirStart.java&amp;lt;/code&amp;gt;||&amp;lt;code&amp;gt;AirStartConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Save some simulation values as a CSV file||&amp;lt;code&amp;gt;CSVSave.java&amp;lt;/code&amp;gt;||&#039;(none)&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Calculate damping moment coefficient after every simulation step|&amp;lt;code&amp;gt;DampingMoment.java&amp;lt;/code&amp;gt;||&#039;(none)&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Print summary of simulation progress after each step||&amp;lt;code&amp;gt;PrintSimulation.java&amp;lt;/code&amp;gt;||&#039;(none)&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Active roll control||&amp;lt;code&amp;gt;RollControl.java&amp;lt;/code&amp;gt;||&amp;lt;code&amp;gt;RollControlConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Stop simulation at specified time or number of steps||&amp;lt;code&amp;gt;StopSimulation&amp;lt;/code&amp;gt;||&amp;lt;code&amp;gt;StopSimulationConfigurator&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35809</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35809"/>
		<updated>2023-01-26T19:18:50Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt; (so it isn&#039;t necessary to call a constructor yourself). &amp;lt;code&amp;gt;core/src/net/sf/openrocket/util/Config.java&amp;lt;/code&amp;gt; includes methods to interact with a configurator, allowing the extension to obtain &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt;, and other configuration values.&lt;br /&gt;
&lt;br /&gt;
In this case, we&#039;ll only be defining a single configuration field in our configurator, &amp;lt;code&amp;gt;&amp;quot;launchAltitude&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; method obtains the air-start altitude for the simulation from the configuration, and sets a default air-start altitude of 1000 meters. Our &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method has been modified to make use of this to obtain the user&#039;s requested air-start altitude from the configurator, in place of the original hard-coded value.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; method places a new launch altitude in the configuration.  While our extension doesn&#039;t make use of this method directly, it will be necessary for our configurator. The call to &amp;lt;code&amp;gt;fireChangeEvent()&amp;lt;/code&amp;gt; in this method assures that the changes we make to the air-start altitude are propagated throughout the simulation.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most important things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier. The name of the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;, &#039;&#039;&#039;&#039;&#039;MUST&#039;&#039;&#039;&#039;&#039; match the names of the corresponding &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; methods exactly.  If they don&#039;t, there will be an exception at run time when the user attempts to change the value.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are provided by the system.&lt;br /&gt;
====Example User Extensions Provided With OpenRocket====&lt;br /&gt;
Several examples of user extensions are provided in the OpenRocket source tree.  As mentioned previously, the extensions are all located in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt; and their configurators are all located in &amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
! Header text !! Header text !! Header text&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
!Purpose!!Top Menu Level!!Bottom Menu Level!!Extension!!Configurator&lt;br /&gt;
|-&lt;br /&gt;
| Set Air-start Altitude and Velocity||Launch Conditions||Air-Start||&amp;lt;code&amp;gt;AirStart.java&amp;lt;/code&amp;gt;||&amp;lt;code&amp;gt;/AirStartConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35808</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35808"/>
		<updated>2023-01-26T19:17:26Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt; (so it isn&#039;t necessary to call a constructor yourself). &amp;lt;code&amp;gt;core/src/net/sf/openrocket/util/Config.java&amp;lt;/code&amp;gt; includes methods to interact with a configurator, allowing the extension to obtain &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt;, and other configuration values.&lt;br /&gt;
&lt;br /&gt;
In this case, we&#039;ll only be defining a single configuration field in our configurator, &amp;lt;code&amp;gt;&amp;quot;launchAltitude&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; method obtains the air-start altitude for the simulation from the configuration, and sets a default air-start altitude of 1000 meters. Our &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method has been modified to make use of this to obtain the user&#039;s requested air-start altitude from the configurator, in place of the original hard-coded value.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; method places a new launch altitude in the configuration.  While our extension doesn&#039;t make use of this method directly, it will be necessary for our configurator. The call to &amp;lt;code&amp;gt;fireChangeEvent()&amp;lt;/code&amp;gt; in this method assures that the changes we make to the air-start altitude are propagated throughout the simulation.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most important things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier. The name of the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;, &#039;&#039;&#039;&#039;&#039;MUST&#039;&#039;&#039;&#039;&#039; match the names of the corresponding &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; methods exactly.  If they don&#039;t, there will be an exception at run time when the user attempts to change the value.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are provided by the system.&lt;br /&gt;
====Example User Extensions Provided With OpenRocket====&lt;br /&gt;
Several examples of user extensions are provided in the OpenRocket source tree.  As mentioned previously, the extensions are all located in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt; and their configurators are all located in &amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
! Header text !! Header text !! Header text&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
!Purpose!!Top Menu Level!!Bottom Menu Level!!Extension!!Configurator&lt;br /&gt;
|-&lt;br /&gt;
|Set Air-start Altitude and Velocity|Launch Conditions|Air-Start|&amp;lt;code&amp;gt;AirStart.java&amp;lt;/code&amp;gt;|&amp;lt;code&amp;gt;/AirStartConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35807</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35807"/>
		<updated>2023-01-26T19:16:32Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt; (so it isn&#039;t necessary to call a constructor yourself). &amp;lt;code&amp;gt;core/src/net/sf/openrocket/util/Config.java&amp;lt;/code&amp;gt; includes methods to interact with a configurator, allowing the extension to obtain &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt;, and other configuration values.&lt;br /&gt;
&lt;br /&gt;
In this case, we&#039;ll only be defining a single configuration field in our configurator, &amp;lt;code&amp;gt;&amp;quot;launchAltitude&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; method obtains the air-start altitude for the simulation from the configuration, and sets a default air-start altitude of 1000 meters. Our &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method has been modified to make use of this to obtain the user&#039;s requested air-start altitude from the configurator, in place of the original hard-coded value.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; method places a new launch altitude in the configuration.  While our extension doesn&#039;t make use of this method directly, it will be necessary for our configurator. The call to &amp;lt;code&amp;gt;fireChangeEvent()&amp;lt;/code&amp;gt; in this method assures that the changes we make to the air-start altitude are propagated throughout the simulation.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most important things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier. The name of the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;, &#039;&#039;&#039;&#039;&#039;MUST&#039;&#039;&#039;&#039;&#039; match the names of the corresponding &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; methods exactly.  If they don&#039;t, there will be an exception at run time when the user attempts to change the value.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are provided by the system.&lt;br /&gt;
====Example User Extensions Provided With OpenRocket====&lt;br /&gt;
Several examples of user extensions are provided in the OpenRocket source tree.  As mentioned previously, the extensions are all located in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt; and their configurators are all located in &amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
! Header text !! Header text !! Header text&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
!Purpose!!Top Menu Level!!Bottom Menu Level!!Extension!!Configurator&lt;br /&gt;
|-&lt;br /&gt;
Set Air-start Altitude and Velocity|Launch Conditions|Air-Start|&amp;lt;code&amp;gt;AirStart.java&amp;lt;/code&amp;gt;|&amp;lt;code&amp;gt;/AirStartConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35806</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35806"/>
		<updated>2023-01-26T19:15:27Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt; (so it isn&#039;t necessary to call a constructor yourself). &amp;lt;code&amp;gt;core/src/net/sf/openrocket/util/Config.java&amp;lt;/code&amp;gt; includes methods to interact with a configurator, allowing the extension to obtain &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt;, and other configuration values.&lt;br /&gt;
&lt;br /&gt;
In this case, we&#039;ll only be defining a single configuration field in our configurator, &amp;lt;code&amp;gt;&amp;quot;launchAltitude&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; method obtains the air-start altitude for the simulation from the configuration, and sets a default air-start altitude of 1000 meters. Our &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method has been modified to make use of this to obtain the user&#039;s requested air-start altitude from the configurator, in place of the original hard-coded value.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; method places a new launch altitude in the configuration.  While our extension doesn&#039;t make use of this method directly, it will be necessary for our configurator. The call to &amp;lt;code&amp;gt;fireChangeEvent()&amp;lt;/code&amp;gt; in this method assures that the changes we make to the air-start altitude are propagated throughout the simulation.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most important things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier. The name of the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;, &#039;&#039;&#039;&#039;&#039;MUST&#039;&#039;&#039;&#039;&#039; match the names of the corresponding &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; methods exactly.  If they don&#039;t, there will be an exception at run time when the user attempts to change the value.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are provided by the system.&lt;br /&gt;
====Example User Extensions Provided With OpenRocket====&lt;br /&gt;
Several examples of user extensions are provided in the OpenRocket source tree.  As mentioned previously, the extensions are all located in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt; and their configurators are all located in &amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
! Header text !! Header text !! Header text&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
!Purpose!!Top Menu Level!!Bottom Menu Level!!Extension!!Configurator&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
Set Air-start Altitude and Velocity|Launch Conditions|Air-Start|&amp;lt;code&amp;gt;AirStart.java&amp;lt;/code&amp;gt;|&amp;lt;code&amp;gt;/AirStartConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35805</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35805"/>
		<updated>2023-01-26T19:14:41Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt; (so it isn&#039;t necessary to call a constructor yourself). &amp;lt;code&amp;gt;core/src/net/sf/openrocket/util/Config.java&amp;lt;/code&amp;gt; includes methods to interact with a configurator, allowing the extension to obtain &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt;, and other configuration values.&lt;br /&gt;
&lt;br /&gt;
In this case, we&#039;ll only be defining a single configuration field in our configurator, &amp;lt;code&amp;gt;&amp;quot;launchAltitude&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; method obtains the air-start altitude for the simulation from the configuration, and sets a default air-start altitude of 1000 meters. Our &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method has been modified to make use of this to obtain the user&#039;s requested air-start altitude from the configurator, in place of the original hard-coded value.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; method places a new launch altitude in the configuration.  While our extension doesn&#039;t make use of this method directly, it will be necessary for our configurator. The call to &amp;lt;code&amp;gt;fireChangeEvent()&amp;lt;/code&amp;gt; in this method assures that the changes we make to the air-start altitude are propagated throughout the simulation.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most important things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier. The name of the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;, &#039;&#039;&#039;&#039;&#039;MUST&#039;&#039;&#039;&#039;&#039; match the names of the corresponding &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; methods exactly.  If they don&#039;t, there will be an exception at run time when the user attempts to change the value.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are provided by the system.&lt;br /&gt;
====Example User Extensions Provided With OpenRocket====&lt;br /&gt;
Several examples of user extensions are provided in the OpenRocket source tree.  As mentioned previously, the extensions are all located in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt; and their configurators are all located in &amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
! Header text !! Header text !! Header text&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{|!Purpose!!Top Menu Level!!Bottom Menu Level!!Extension!!Configurator&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
Set Air-start Altitude and Velocity|Launch Conditions|Air-Start|&amp;lt;code&amp;gt;AirStart.java&amp;lt;/code&amp;gt;|&amp;lt;code&amp;gt;/AirStartConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35804</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35804"/>
		<updated>2023-01-26T19:13:11Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt; (so it isn&#039;t necessary to call a constructor yourself). &amp;lt;code&amp;gt;core/src/net/sf/openrocket/util/Config.java&amp;lt;/code&amp;gt; includes methods to interact with a configurator, allowing the extension to obtain &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt;, and other configuration values.&lt;br /&gt;
&lt;br /&gt;
In this case, we&#039;ll only be defining a single configuration field in our configurator, &amp;lt;code&amp;gt;&amp;quot;launchAltitude&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; method obtains the air-start altitude for the simulation from the configuration, and sets a default air-start altitude of 1000 meters. Our &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method has been modified to make use of this to obtain the user&#039;s requested air-start altitude from the configurator, in place of the original hard-coded value.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; method places a new launch altitude in the configuration.  While our extension doesn&#039;t make use of this method directly, it will be necessary for our configurator. The call to &amp;lt;code&amp;gt;fireChangeEvent()&amp;lt;/code&amp;gt; in this method assures that the changes we make to the air-start altitude are propagated throughout the simulation.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most important things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier. The name of the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;, &#039;&#039;&#039;&#039;&#039;MUST&#039;&#039;&#039;&#039;&#039; match the names of the corresponding &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; methods exactly.  If they don&#039;t, there will be an exception at run time when the user attempts to change the value.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are provided by the system.&lt;br /&gt;
====Example User Extensions Provided With OpenRocket====&lt;br /&gt;
Several examples of user extensions are provided in the OpenRocket source tree.  As mentioned previously, the extensions are all located in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt; and their configurators are all located in &amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Header text !! Header text !! Header text&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{|!Purpose!!Top Menu Level!!Bottom Menu Level!!Extension!!Configurator&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
Set Air-start Altitude and Velocity|Launch Conditions|Air-Start|&amp;lt;code&amp;gt;AirStart.java&amp;lt;/code&amp;gt;|&amp;lt;code&amp;gt;/AirStartConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35803</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35803"/>
		<updated>2023-01-26T19:12:12Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt; (so it isn&#039;t necessary to call a constructor yourself). &amp;lt;code&amp;gt;core/src/net/sf/openrocket/util/Config.java&amp;lt;/code&amp;gt; includes methods to interact with a configurator, allowing the extension to obtain &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt;, and other configuration values.&lt;br /&gt;
&lt;br /&gt;
In this case, we&#039;ll only be defining a single configuration field in our configurator, &amp;lt;code&amp;gt;&amp;quot;launchAltitude&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; method obtains the air-start altitude for the simulation from the configuration, and sets a default air-start altitude of 1000 meters. Our &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method has been modified to make use of this to obtain the user&#039;s requested air-start altitude from the configurator, in place of the original hard-coded value.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; method places a new launch altitude in the configuration.  While our extension doesn&#039;t make use of this method directly, it will be necessary for our configurator. The call to &amp;lt;code&amp;gt;fireChangeEvent()&amp;lt;/code&amp;gt; in this method assures that the changes we make to the air-start altitude are propagated throughout the simulation.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most important things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier. The name of the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;, &#039;&#039;&#039;&#039;&#039;MUST&#039;&#039;&#039;&#039;&#039; match the names of the corresponding &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; methods exactly.  If they don&#039;t, there will be an exception at run time when the user attempts to change the value.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are provided by the system.&lt;br /&gt;
====Example User Extensions Provided With OpenRocket====&lt;br /&gt;
Several examples of user extensions are provided in the OpenRocket source tree.  As mentioned previously, the extensions are all located in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt; and their configurators are all located in &amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Header text !! Header text !! Header text&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{|!Purpose!!Top Menu Level!!Bottom Menu Level!!Extension!!Configurator&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
Set Air-start Altitude and Velocity|Launch Conditions|Air-Start|&amp;lt;code&amp;gt;AirStart.java&amp;lt;/code&amp;gt;|&amp;lt;code&amp;gt;/AirStartConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35802</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35802"/>
		<updated>2023-01-26T19:11:14Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt; (so it isn&#039;t necessary to call a constructor yourself). &amp;lt;code&amp;gt;core/src/net/sf/openrocket/util/Config.java&amp;lt;/code&amp;gt; includes methods to interact with a configurator, allowing the extension to obtain &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt;, and other configuration values.&lt;br /&gt;
&lt;br /&gt;
In this case, we&#039;ll only be defining a single configuration field in our configurator, &amp;lt;code&amp;gt;&amp;quot;launchAltitude&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; method obtains the air-start altitude for the simulation from the configuration, and sets a default air-start altitude of 1000 meters. Our &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method has been modified to make use of this to obtain the user&#039;s requested air-start altitude from the configurator, in place of the original hard-coded value.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; method places a new launch altitude in the configuration.  While our extension doesn&#039;t make use of this method directly, it will be necessary for our configurator. The call to &amp;lt;code&amp;gt;fireChangeEvent()&amp;lt;/code&amp;gt; in this method assures that the changes we make to the air-start altitude are propagated throughout the simulation.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most important things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier. The name of the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;, &#039;&#039;&#039;&#039;&#039;MUST&#039;&#039;&#039;&#039;&#039; match the names of the corresponding &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; methods exactly.  If they don&#039;t, there will be an exception at run time when the user attempts to change the value.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are provided by the system.&lt;br /&gt;
====Example User Extensions Provided With OpenRocket====&lt;br /&gt;
Several examples of user extensions are provided in the OpenRocket source tree.  As mentioned previously, the extensions are all located in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt; and their configurators are all located in &amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Caption text&lt;br /&gt;
|-&lt;br /&gt;
! Header text !! Header text !! Header text&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{|!Purpose!!Top Menu Level!!Bottom Menu Level!!Extension!!Configurator&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
Set Air-start Altitude and Velocity|Launch Conditions|Air-Start|&amp;lt;code&amp;gt;AirStart.java&amp;lt;/code&amp;gt;|&amp;lt;code&amp;gt;/AirStartConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35801</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35801"/>
		<updated>2023-01-26T19:10:13Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt; (so it isn&#039;t necessary to call a constructor yourself). &amp;lt;code&amp;gt;core/src/net/sf/openrocket/util/Config.java&amp;lt;/code&amp;gt; includes methods to interact with a configurator, allowing the extension to obtain &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt;, and other configuration values.&lt;br /&gt;
&lt;br /&gt;
In this case, we&#039;ll only be defining a single configuration field in our configurator, &amp;lt;code&amp;gt;&amp;quot;launchAltitude&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; method obtains the air-start altitude for the simulation from the configuration, and sets a default air-start altitude of 1000 meters. Our &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method has been modified to make use of this to obtain the user&#039;s requested air-start altitude from the configurator, in place of the original hard-coded value.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; method places a new launch altitude in the configuration.  While our extension doesn&#039;t make use of this method directly, it will be necessary for our configurator. The call to &amp;lt;code&amp;gt;fireChangeEvent()&amp;lt;/code&amp;gt; in this method assures that the changes we make to the air-start altitude are propagated throughout the simulation.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most important things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier. The name of the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;, &#039;&#039;&#039;&#039;&#039;MUST&#039;&#039;&#039;&#039;&#039; match the names of the corresponding &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; methods exactly.  If they don&#039;t, there will be an exception at run time when the user attempts to change the value.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are provided by the system.&lt;br /&gt;
====Example User Extensions Provided With OpenRocket====&lt;br /&gt;
Several examples of user extensions are provided in the OpenRocket source tree.  As mentioned previously, the extensions are all located in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt; and their configurators are all located in &amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|!Purpose!!Top Menu Level!!Bottom Menu Level!!Extension!!Configurator&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
Set Air-start Altitude and Velocity|Launch Conditions|Air-Start|&amp;lt;code&amp;gt;AirStart.java&amp;lt;/code&amp;gt;|&amp;lt;code&amp;gt;/AirStartConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35800</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35800"/>
		<updated>2023-01-26T19:08:58Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt; (so it isn&#039;t necessary to call a constructor yourself). &amp;lt;code&amp;gt;core/src/net/sf/openrocket/util/Config.java&amp;lt;/code&amp;gt; includes methods to interact with a configurator, allowing the extension to obtain &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt;, and other configuration values.&lt;br /&gt;
&lt;br /&gt;
In this case, we&#039;ll only be defining a single configuration field in our configurator, &amp;lt;code&amp;gt;&amp;quot;launchAltitude&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; method obtains the air-start altitude for the simulation from the configuration, and sets a default air-start altitude of 1000 meters. Our &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method has been modified to make use of this to obtain the user&#039;s requested air-start altitude from the configurator, in place of the original hard-coded value.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; method places a new launch altitude in the configuration.  While our extension doesn&#039;t make use of this method directly, it will be necessary for our configurator. The call to &amp;lt;code&amp;gt;fireChangeEvent()&amp;lt;/code&amp;gt; in this method assures that the changes we make to the air-start altitude are propagated throughout the simulation.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most important things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier. The name of the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;, &#039;&#039;&#039;&#039;&#039;MUST&#039;&#039;&#039;&#039;&#039; match the names of the corresponding &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; methods exactly.  If they don&#039;t, there will be an exception at run time when the user attempts to change the value.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are provided by the system.&lt;br /&gt;
====Example User Extensions Provided With OpenRocket====&lt;br /&gt;
Several examples of user extensions are provided in the OpenRocket source tree.  As mentioned previously, the extensions are all located in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt; and their configurators are all located in &amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|!Purpose!!Top Menu Level!!Bottom Menu Level!!Extension!!Configurator&lt;br /&gt;
|-&lt;br /&gt;
Set Air-start Altitude and Velocity|Launch Conditions|Air-Start|&amp;lt;code&amp;gt;AirStart.java&amp;lt;/code&amp;gt;|&amp;lt;code&amp;gt;/AirStartConfigurator.java&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35799</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35799"/>
		<updated>2023-01-26T18:56:39Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt; (so it isn&#039;t necessary to call a constructor yourself). &amp;lt;code&amp;gt;core/src/net/sf/openrocket/util/Config.java&amp;lt;/code&amp;gt; includes methods to interact with a configurator, allowing the extension to obtain &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt;, and other configuration values.&lt;br /&gt;
&lt;br /&gt;
In this case, we&#039;ll only be defining a single configuration field in our configurator, &amp;lt;code&amp;gt;&amp;quot;launchAltitude&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; method obtains the air-start altitude for the simulation from the configuration, and sets a default air-start altitude of 1000 meters. Our &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method has been modified to make use of this to obtain the user&#039;s requested air-start altitude from the configurator, in place of the original hard-coded value.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; method places a new launch altitude in the configuration.  While our extension doesn&#039;t make use of this method directly, it will be necessary for our configurator. The call to &amp;lt;code&amp;gt;fireChangeEvent()&amp;lt;/code&amp;gt; in this method assures that the changes we make to the air-start altitude are propagated throughout the simulation.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most important things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier. The name of the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;, &#039;&#039;&#039;&#039;&#039;MUST&#039;&#039;&#039;&#039;&#039; match the names of the corresponding &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; methods exactly.  If they don&#039;t, there will be an exception at run time when the user attempts to change the value.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are all provided by the system.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35798</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35798"/>
		<updated>2023-01-26T18:50:33Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt; (so it isn&#039;t necessary to call a constructor yourself). &amp;lt;code&amp;gt;core/src/net/sf/openrocket/util/Config.java&amp;lt;/code&amp;gt; includes methods to interact with a configurator, allowing the extension to obtain &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt;, and other configuration values.&lt;br /&gt;
&lt;br /&gt;
In this case, we&#039;ll only be defining a single configuration field in our configurator, &amp;lt;code&amp;gt;&amp;quot;launchAltitude&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; method obtains the air-start altitude for the simulation from the configuration, and sets a default air-start altitude of 1000 meters. Our &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method has been modified to make use of this to obtain the user&#039;s requested air-start altitude from the configurator, in place of the original hard-coded value.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; method places a new launch altitude in the configuration.  While our extension doesn&#039;t make use of this method directly, it will be necessary for our configurator. The call to &amp;lt;code&amp;gt;fireChangeEvent()&amp;lt;/code&amp;gt; in this method assures that the changes we make to the air-start altitude are propagated throughout the simulation.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier; of course, they have to match.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this DoubleModel.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are all provided by the system.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35797</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35797"/>
		<updated>2023-01-26T18:49:47Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt; (so it isn&#039;t necessary to call a constructor yourself). &amp;lt;code&amp;gt;core/src/net/sf/openrocket/util/Config.java&amp;lt;/code&amp;gt; includes methods to interact with a configurator, allowing the extension to obtain &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;string&amp;lt;/code&amp;gt;, and other configuration values.&lt;br /&gt;
&lt;br /&gt;
In this case, we&#039;ll only be defining a single configuration field in our configurator, &amp;lt;code&amp;gt;&amp;quot;launchAltitude&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; method obtains the air-start altitude for the simulation from the configuration, and sets a default air-start altitude of 1000 meters. Our &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method has been modified to make use of this to obtain the user&#039;s requested air-start altitude from the configurator, in place of the original hard-coded value.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; method places a new launch altitude in the configuration.  While our extension doesn&#039;t make use of this method directly, it will be necessary for our configurator. The call to &amp;lt;code&amp;gt;fireChangeEvent()&amp;lt;/code&amp;gt; in this method assures that the changes we make to the air-start altitude are propagated throughout the simulation.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
42&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier; of course, they have to match.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this DoubleModel.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are all provided by the system.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35796</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35796"/>
		<updated>2023-01-26T18:26:07Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
45&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
One thing to notice is that the call to &amp;lt;code&amp;gt;config.getDouble()&amp;lt;/code&amp;gt;, the value &amp;lt;code&amp;gt;1000.0&amp;lt;/code&amp;gt; is the default airstart altitude (in meters).  This is the value that will appear when the configurator is first opened.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier; of course, they have to match.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this DoubleModel.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are all provided by the system.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35795</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35795"/>
		<updated>2023-01-26T18:25:27Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method in lines 14-16, which adds the listener to the simulations &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method in lines 19-21, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example, &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would have returned &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if this method hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method in lines 24-26, which provides a brief description of the purpose of the extension. This is the method that provides the text for the &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button dialog shown in the first section of this page.&lt;br /&gt;
* The listener itself in lines 28-34, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu.  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first menu level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension First, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
38&lt;br /&gt;
39&lt;br /&gt;
40&lt;br /&gt;
41&lt;br /&gt;
42&lt;br /&gt;
43&lt;br /&gt;
44&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
One thing to notice is that the call to &amp;lt;code&amp;gt;config.getDouble()&amp;lt;/code&amp;gt;, the value &amp;lt;code&amp;gt;1000.0&amp;lt;/code&amp;gt; is the default airstart altitude (in meters).  This is the value that will appear when the configurator is first opened.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier; of course, they have to match.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this DoubleModel.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are all provided by the system.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35794</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35794"/>
		<updated>2023-01-26T18:17:13Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code&amp;gt; extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example,&lt;br /&gt;
&amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would return &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method, which provides a brief description of the purpose of the extension. This is the method that provides the text for the an &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button in the first section of this page.&lt;br /&gt;
* The listener itself, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension, first, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
One thing to notice is that the call to &amp;lt;code&amp;gt;config.getDouble()&amp;lt;/code&amp;gt;, the value &amp;lt;code&amp;gt;1000.0&amp;lt;/code&amp;gt; is the default airstart altitude (in meters).  This is the value that will appear when the configurator is first opened.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier; of course, they have to match.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this DoubleModel.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are all provided by the system.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35793</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35793"/>
		<updated>2023-01-26T18:16:13Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example,&lt;br /&gt;
&amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would return &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method, which provides a brief description of the purpose of the extension. This is the method that provides the text for the an &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button in the first section of this page.&lt;br /&gt;
* The listener itself, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension, first, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
One thing to notice is that the call to &amp;lt;code&amp;gt;config.getDouble()&amp;lt;/code&amp;gt;, the value &amp;lt;code&amp;gt;1000.0&amp;lt;/code&amp;gt; is the default airstart altitude (in meters).  This is the value that will appear when the configurator is first opened.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier; of course, they have to match.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this DoubleModel.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are all provided by the system.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35792</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35792"/>
		<updated>2023-01-26T18:15:18Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
37&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example,&lt;br /&gt;
&amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would return &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method, which provides a brief description of the purpose of the extension. This is the method that provides the text for the an &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button in the first section of this page.&lt;br /&gt;
* The listener itself, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension, first, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
One thing to notice is that the call to &amp;lt;code&amp;gt;config.getDouble()&amp;lt;/code&amp;gt;, the value &amp;lt;code&amp;gt;1000.0&amp;lt;/code&amp;gt; is the default airstart altitude (in meters).  This is the value that will appear when the configurator is first opened.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier; of course, they have to match.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this DoubleModel.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are all provided by the system.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35791</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35791"/>
		<updated>2023-01-26T18:14:19Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
31&lt;br /&gt;
32&lt;br /&gt;
33&lt;br /&gt;
34&lt;br /&gt;
35&lt;br /&gt;
36&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example,&lt;br /&gt;
&amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would return &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method, which provides a brief description of the purpose of the extension. This is the method that provides the text for the an &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button in the first section of this page.&lt;br /&gt;
* The listener itself, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension, first, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
One thing to notice is that the call to &amp;lt;code&amp;gt;config.getDouble()&amp;lt;/code&amp;gt;, the value &amp;lt;code&amp;gt;1000.0&amp;lt;/code&amp;gt; is the default airstart altitude (in meters).  This is the value that will appear when the configurator is first opened.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier; of course, they have to match.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this DoubleModel.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are all provided by the system.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35790</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35790"/>
		<updated>2023-01-26T18:10:10Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;.  Note that while some information can be obtained in this way, it is not as complete as that found in &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
====Flight Data====&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt; objects with one list for each simulation variable and one element in the list for each time step.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; lists are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; if needed for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The simulation data for each stage of the rocket&#039;s flight is referred to as a &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;.  Every simulation has at least one &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt; for its sustainer, and will have additional branches for its boosters.&lt;br /&gt;
&lt;br /&gt;
Finally, the collection of all of the &amp;lt;code&amp;gt;FlightDataBranch&amp;lt;/code&amp;gt;es and some summary data for the simulation is stored in a &amp;lt;code&amp;gt;FlightData&amp;lt;/code&amp;gt; object.  &lt;br /&gt;
&lt;br /&gt;
====Flight Conditions====&lt;br /&gt;
Current data regarding the aerodynamics of the flight itself are stored in a &amp;lt;code&amp;gt;FlightConditions&amp;lt;/code&amp;gt; object.  This includes things like the velocity, angle of attack, and roll and pitch angle and rates. It also contains a reference to the current &amp;lt;code&amp;gt;AtmosphericConditions&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status to simulate an air-start.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation()&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep()&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep()&amp;lt;/code&amp;gt; and event-related hook methods return a boolean value indicating whether the associated action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may also have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation (not a bug), and an error dialog is displayed to the user with the exception message.  The simulation data produced thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation. Your listener can (and ordinarily will) be private to your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and make sure it is in a directory that is in your Java classpath when OpenRocket is executed, or you can insert it in the source tree and compile it along with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your own, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extension examples provided with OpenRocket are located.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time. This is a simplified version of the &amp;lt;code&amp;gt;AirStart&amp;lt;/code extension located in the OpenRocket source code tree; that class also sets a start velocity.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example,&lt;br /&gt;
&amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would return &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method, which provides a brief description of the purpose of the extension. This is the method that provides the text for the an &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button in the first section of this page.&lt;br /&gt;
* The listener itself, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension, first, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
One thing to notice is that the call to &amp;lt;code&amp;gt;config.getDouble()&amp;lt;/code&amp;gt;, the value &amp;lt;code&amp;gt;1000.0&amp;lt;/code&amp;gt; is the default airstart altitude (in meters).  This is the value that will appear when the configurator is first opened.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier; of course, they have to match.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this DoubleModel.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are all provided by the system.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35779</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35779"/>
		<updated>2022-12-13T04:50:45Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
====Flight Data====&lt;br /&gt;
The results from executing a simulation is stored in a &lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example,&lt;br /&gt;
&amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would return &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method, which provides a brief description of the purpose of the extension. This is the method that provides the text for the an &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button in the first section of this page.&lt;br /&gt;
* The listener itself, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension, first, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
One thing to notice is that the call to &amp;lt;code&amp;gt;config.getDouble()&amp;lt;/code&amp;gt;, the value &amp;lt;code&amp;gt;1000.0&amp;lt;/code&amp;gt; is the default airstart altitude (in meters).  This is the value that will appear when the configurator is first opened.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier; of course, they have to match.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this DoubleModel.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
&lt;br /&gt;
The surrounding Dialog window and the &#039;&#039;&#039;Close&#039;&#039;&#039; button are all provided by the system.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35778</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35778"/>
		<updated>2022-12-12T23:22:34Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
The remainder of this page will describe how a new simulation extension is created.&lt;br /&gt;
&lt;br /&gt;
===Preliminary Concepts===&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
====Simulation Status====&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Simulation Listeners====&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example,&lt;br /&gt;
&amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would return &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method, which provides a brief description of the purpose of the extension. This is the method that provides the text for the an &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button in the first section of this page.&lt;br /&gt;
* The listener itself, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension, first, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
One thing to notice is that the call to &amp;lt;code&amp;gt;config.getDouble()&amp;lt;/code&amp;gt;, the value &amp;lt;code&amp;gt;1000.0&amp;lt;/code&amp;gt; is the default airstart altitude (in meters).  This is the value that will appear when the configurator is first opened.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier; of course, they have to match.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this DoubleModel.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
===Flight Data===&lt;br /&gt;
&lt;br /&gt;
The results from executing a simulation is stored in a &lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35777</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35777"/>
		<updated>2022-12-12T23:19:04Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
===Simulation Status===&lt;br /&gt;
&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simulation Listeners===&lt;br /&gt;
&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example,&lt;br /&gt;
&amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would return &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method, which provides a brief description of the purpose of the extension. This is the method that provides the text for the an &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button in the first section of this page.&lt;br /&gt;
* The listener itself, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension, first, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
One thing to notice is that the call to &amp;lt;code&amp;gt;config.getDouble()&amp;lt;/code&amp;gt;, the value &amp;lt;code&amp;gt;1000.0&amp;lt;/code&amp;gt; is the default airstart altitude (in meters).  This is the value that will appear when the configurator is first opened.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most things to notice about the &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; constructor are the parameters &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;  is used by the system to synthesize calls to the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier; of course, they have to match.&lt;br /&gt;
* &amp;lt;code&amp;gt;UnitGroup.UNITS_DISTANCE&amp;lt;/code&amp;gt; specifies the unit group to be used by this &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt;. OpenRocket uses SI (MKS) units internally, but allows users to select the units they wish to use for their interface.  Specifying a &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt; provides the conversions and unit displays for the interface.  The available &amp;lt;code&amp;gt;UnitGroup&amp;lt;/code&amp;gt;s are defined in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/unit/UnitGroup.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a &amp;lt;code&amp;gt;JSpinner&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;UnitSelector&amp;lt;/code&amp;gt;, and a &amp;lt;code&amp;gt;BasicSlider&amp;lt;/code&amp;gt; all referring to this DoubleModel.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
===Flight Data===&lt;br /&gt;
&lt;br /&gt;
The results from executing a simulation is stored in a &lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35776</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35776"/>
		<updated>2022-12-12T23:10:29Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
===Simulation Status===&lt;br /&gt;
&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simulation Listeners===&lt;br /&gt;
&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example,&lt;br /&gt;
&amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would return &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method, which provides a brief description of the purpose of the extension. This is the method that provides the text for the an &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button in the first section of this page.&lt;br /&gt;
* The listener itself, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension, first, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
One thing to notice is that the call to &amp;lt;code&amp;gt;config.getDouble()&amp;lt;/code&amp;gt;, the value &amp;lt;code&amp;gt;1000.0&amp;lt;/code&amp;gt; is the default airstart altitude (in meters).  This is the value that will appear when the configurator is first opened.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 5000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most thing to notice about the constructor is the parameter &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;.  This has to match the names of the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier; the system uses this string to synthesize calls to those methods.&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a JSpinner, a UnitSelector, and a BasicSlider all referring to this DoubleModel.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
===Flight Data===&lt;br /&gt;
&lt;br /&gt;
The results from executing a simulation is stored in a &lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35775</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35775"/>
		<updated>2022-12-12T23:06:34Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
===Simulation Status===&lt;br /&gt;
&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simulation Listeners===&lt;br /&gt;
&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example,&lt;br /&gt;
&amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would return &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method, which provides a brief description of the purpose of the extension. This is the method that provides the text for the an &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button in the first section of this page.&lt;br /&gt;
* The listener itself, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension, first, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
One thing to notice is that the call to &amp;lt;code&amp;gt;config.getDouble()&amp;lt;/code&amp;gt;, the value &amp;lt;code&amp;gt;1000.0&amp;lt;/code&amp;gt; is the default airstart altitude (in meters).  This is the value that will appear when the configurator is first opened.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 1000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most thing to notice about the constructor is the parameter &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;.  This has to match the names of the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier; the system uses this string to synthesize calls to those methods.&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a JSpinner, a UnitSelector, and a BasicSlider all referring to this DoubleModel.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:Example_Configurator.png]]&lt;br /&gt;
===Flight Data===&lt;br /&gt;
&lt;br /&gt;
The results from executing a simulation is stored in a &lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=File:Example_Configurator.png&amp;diff=35774</id>
		<title>File:Example Configurator.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=File:Example_Configurator.png&amp;diff=35774"/>
		<updated>2022-12-12T23:02:40Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: Configurator for AirStart example&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Configurator for AirStart example&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35773</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35773"/>
		<updated>2022-12-12T22:01:18Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: /* Adding a Configurator */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
===Simulation Status===&lt;br /&gt;
&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simulation Listeners===&lt;br /&gt;
&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example,&lt;br /&gt;
&amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would return &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method, which provides a brief description of the purpose of the extension. This is the method that provides the text for the an &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button in the first section of this page.&lt;br /&gt;
* The listener itself, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension, first, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
One thing to notice is that the call to &amp;lt;code&amp;gt;config.getDouble()&amp;lt;/code&amp;gt;, the value &amp;lt;code&amp;gt;1000.0&amp;lt;/code&amp;gt; is the default airstart altitude (in meters).  This is the value that will appear when the configurator is first opened.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 1000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most thing to notice about the constructor is the parameter &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;.  This has to match the names of the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier; the system uses this string to synthesize calls to those methods.&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a JSpinner, a UnitSelector, and a BasicSlider all referring to this DoubleModel.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
===Flight Data===&lt;br /&gt;
&lt;br /&gt;
The results from executing a simulation is stored in a &lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35772</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35772"/>
		<updated>2022-12-12T16:59:33Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: /* Extension Example */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
===Simulation Status===&lt;br /&gt;
&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simulation Listeners===&lt;br /&gt;
&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Extension Example====&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example,&lt;br /&gt;
&amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would return &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method, which provides a brief description of the purpose of the extension. This is the method that provides the text for the an &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button in the first section of this page.&lt;br /&gt;
* The listener itself, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension, first, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
One thing to notice is that the call to &amp;lt;code&amp;gt;config.getDouble()&amp;lt;/code&amp;gt;, the value &amp;lt;code&amp;gt;1000.0&amp;lt;/code&amp;gt; is the default airstart altitude (in meters).  This is the value that will appear when the configurator is first opened.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 1000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most thing to notice about the constructor is the parameter &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;.  This has to match the names of the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code.setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier; the system uses this string to synthesize calls to those methods.&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a JSpinner, a UnitSelector, and a BasicSlider all referring to this DoubleModel.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
===Flight Data===&lt;br /&gt;
&lt;br /&gt;
The results from executing a simulation is stored in a &lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35771</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35771"/>
		<updated>2022-12-12T04:41:14Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
===Simulation Status===&lt;br /&gt;
&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simulation Listeners===&lt;br /&gt;
&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Extension Example===&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example,&lt;br /&gt;
&amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would return &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method, which provides a brief description of the purpose of the extension. This is the method that provides the text for the an &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button in the first section of this page.&lt;br /&gt;
* The listener itself, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension, first, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
One thing to notice is that the call to &amp;lt;code&amp;gt;config.getDouble()&amp;lt;/code&amp;gt;, the value &amp;lt;code&amp;gt;1000.0&amp;lt;/code&amp;gt; is the default airstart altitude (in meters).  This is the value that will appear when the configurator is first opened.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 1000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The most thing to notice about the constructor is the parameter &amp;lt;code&amp;gt;&amp;quot;LaunchAltitude&amp;quot;&amp;lt;/code&amp;gt;.  This has to match the names of the &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code.setLaunchAltitude()&amp;lt;/code&amp;gt; methods mentioned earlier; the system uses this string to synthesize calls to those methods.&lt;br /&gt;
&lt;br /&gt;
The remaining code in this method creates a JSpinner, a UnitSelector, and a BasicSlider all referring to this DoubleModel.  When the resulting configurator is displayed, it looks like this:&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
===Flight Data===&lt;br /&gt;
&lt;br /&gt;
The results from executing a simulation is stored in a &lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35770</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35770"/>
		<updated>2022-12-12T02:56:23Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
===Simulation Status===&lt;br /&gt;
&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code&amp;gt;; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simulation Listeners===&lt;br /&gt;
&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Extension Example===&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example,&lt;br /&gt;
&amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would return &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method, which provides a brief description of the purpose of the extension. This is the method that provides the text for the an &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button in the first section of this page.&lt;br /&gt;
* The listener itself, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension, first, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener to obtain the configured launch altitude. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
One thing to notice is that the call to &amp;lt;code&amp;gt;config.getDouble()&amp;lt;/code&amp;gt;, the value &amp;lt;code&amp;gt;1000.0&amp;lt;/code&amp;gt; is the default airstart altitude (in meters).  This is the value that will appear when the configurator is first opened.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 1000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After some boilerplate, this class creates a new &amp;lt;code&amp;gt;DoubleModel&amp;lt;/code&amp;gt; to manage the airstart altitude.  The parameters have the following meaning:&lt;br /&gt;
&lt;br /&gt;
===Flight Data===&lt;br /&gt;
&lt;br /&gt;
The results from executing a simulation is stored in a &lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35769</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35769"/>
		<updated>2022-12-11T17:31:07Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
===Simulation Status===&lt;br /&gt;
&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simulation Listeners===&lt;br /&gt;
&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Extension Example===&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example,&lt;br /&gt;
&amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would return &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method, which provides a brief description of the purpose of the extension. This is the method that provides the text for the an &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button in the first section of this page.&lt;br /&gt;
* The listener itself, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension, first, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener. &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The configurator itself looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 1000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Flight Data===&lt;br /&gt;
&lt;br /&gt;
The results from executing a simulation is stored in a &lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35768</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35768"/>
		<updated>2022-12-11T17:17:02Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
===Simulation Status===&lt;br /&gt;
&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simulation Listeners===&lt;br /&gt;
&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Extension Example===&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example,&lt;br /&gt;
&amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would return &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method, which provides a brief description of the purpose of the extension. This is the method that provides the text for the an &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button in the first section of this page.&lt;br /&gt;
* The listener itself, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension, first, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 1000.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This adds two methods to the extension (&amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setLaunchAltitude()&amp;lt;/code&amp;gt;), and calls &amp;lt;code&amp;gt;getLaunchAltitude()&amp;lt;/code&amp;gt; from within the listener. &amp;lt;code&amp;gt;config&amp;lt;/code a &amp;lt;code&amp;gt;Config&amp;lt;/code&amp;gt; object, provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The configurator itself extends looks like this&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import javax.swing.JComponent;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JSpinner;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.document.Simulation;&lt;br /&gt;
import net.sf.openrocket.gui.SpinnerEditor;&lt;br /&gt;
import net.sf.openrocket.gui.adaptors.DoubleModel;&lt;br /&gt;
import net.sf.openrocket.gui.components.BasicSlider;&lt;br /&gt;
import net.sf.openrocket.gui.components.UnitSelector;&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;&lt;br /&gt;
import net.sf.openrocket.unit.UnitGroup;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartConfigurator extends AbstractSwingSimulationExtensionConfigurator&amp;lt;AirStart&amp;gt; {&lt;br /&gt;
	&lt;br /&gt;
    public AirStartConfigurator() {&lt;br /&gt;
        super(AirStart.class);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    @Override&lt;br /&gt;
    protected JComponent getConfigurationComponent(AirStart extension, Simulation simulation, JPanel panel) {&lt;br /&gt;
        panel.add(new JLabel(&amp;quot;Launch altitude:&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        DoubleModel m = new DoubleModel(extension, &amp;quot;LaunchAltitude&amp;quot;, UnitGroup.UNITS_DISTANCE, 0);&lt;br /&gt;
&lt;br /&gt;
        JSpinner spin = new JSpinner(m.getSpinnerModel());&lt;br /&gt;
        spin.setEditor(new SpinnerEditor(spin));&lt;br /&gt;
        panel.add(spin, &amp;quot;w 65lp!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        UnitSelector unit = new UnitSelector(m);&lt;br /&gt;
        panel.add(unit, &amp;quot;w 25&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        BasicSlider slider = new BasicSlider(m.getSliderModel(0, 1000));&lt;br /&gt;
        panel.add(slider, &amp;quot;w 75lp, wrap&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
        return panel;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Flight Data===&lt;br /&gt;
&lt;br /&gt;
The results from executing a simulation is stored in a &lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35767</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35767"/>
		<updated>2022-12-11T16:40:25Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
===Simulation Status===&lt;br /&gt;
&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simulation Listeners===&lt;br /&gt;
&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Extension Example===&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example,&lt;br /&gt;
&amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would return &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method, which provides a brief description of the purpose of the extension. This is the method that provides the text for the an &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button in the first section of this page.&lt;br /&gt;
* The listener itself, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension, first, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;bold&amp;gt;&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 0.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/bold&amp;gt;&lt;br /&gt;
        &lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&amp;lt;bold&amp;gt;&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Flight Data===&lt;br /&gt;
&lt;br /&gt;
The results from executing a simulation is stored in a &lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35766</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35766"/>
		<updated>2022-12-11T16:39:01Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
===Simulation Status===&lt;br /&gt;
&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simulation Listeners===&lt;br /&gt;
&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Extension Example===&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a hard-coded altitude.  Later, we&#039;ll add a configurator to the extension so we can set the launch altitude through a GUI at run time.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are several important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; method, which provides the extension&#039;s name.  A default &amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; is provided by &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;, which simply uses the classname (so for this example,&lt;br /&gt;
&amp;lt;code&amp;gt;getName()&amp;lt;/code&amp;gt; would return &amp;lt;code&amp;gt;&amp;quot;AirStartExample&amp;quot;&amp;lt;/code&amp;gt; if hadn&#039;t overridden it).&lt;br /&gt;
* The &amp;lt;code&amp;gt;getDescription()&amp;lt;/code&amp;gt; method, which provides a brief description of the purpose of the extension. This is the method that provides the text for the an &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt; button in the first section of this page.&lt;br /&gt;
* The listener itself, which provides a single &amp;lt;code&amp;gt;startSimulation()&amp;lt;/code&amp;gt; method.  When the simulation for starts executing, this listener is called and the rocket is set to an altitude of 1000 meters. &lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This class adds your extension to the extension menu.  The first &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt;) is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will add it to the first level menu.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
====Adding a Configurator====&lt;br /&gt;
To be able to configure the extension at run time, we need to write a configurator and provide it with a way to communicate with the extension, first, we&#039;ll modify the extension as follows:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
30&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return &amp;quot;Air-Start Example&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public String getDescription() {&lt;br /&gt;
        return &amp;quot;Simple extension example for air-start&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;bold style=&amp;quot;color:darkblue&amp;quot;&amp;gt;&lt;br /&gt;
    public double getLaunchAltitude() {&lt;br /&gt;
        return config.getDouble(&amp;quot;launchAltitude&amp;quot;, 0.0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setLaunchAltitude(double launchAltitude) {&lt;br /&gt;
        config.put(&amp;quot;launchAltitude&amp;quot;, launchAltitude);&lt;br /&gt;
        fireChangeEvent();&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/bold&amp;gt;&lt;br /&gt;
        &lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
&amp;lt;bold&amp;gt;&lt;br /&gt;
            status.setRocketPosition(new Coordinate(0, 0, 1000.0));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Flight Data===&lt;br /&gt;
&lt;br /&gt;
The results from executing a simulation is stored in a &lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35765</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35765"/>
		<updated>2022-12-11T16:07:12Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
===Simulation Status===&lt;br /&gt;
&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simulation Listeners===&lt;br /&gt;
&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a New Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Your Extension===&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a specified position with a specified velocity.  We&#039;ll start with a very simple version, and add features to illustrate the capabilities of simulation extensions.&lt;br /&gt;
&lt;br /&gt;
Our initial extension will simply start the rocket from a specified hard-coded altitude.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
22&lt;br /&gt;
23&lt;br /&gt;
24&lt;br /&gt;
25&lt;br /&gt;
26&lt;br /&gt;
27&lt;br /&gt;
28&lt;br /&gt;
29&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        /** Launch altitude in meters */&lt;br /&gt;
        private static final double ALTITUDE = 1000.0;&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            Coordinate position = status.getRocketPosition();&lt;br /&gt;
            position = position.add(0, 0, ALTITUDE);&lt;br /&gt;
            status.setRocketPosition(position);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are two important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The listener itself.&lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This method adds your extension to the extension menu.  The first string (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt; is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will just add a new first level entry.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Flight Data===&lt;br /&gt;
&lt;br /&gt;
The results from executing a simulation is stored in a &lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35764</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35764"/>
		<updated>2022-12-11T05:02:08Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
===Simulation Status===&lt;br /&gt;
&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simulation Listeners===&lt;br /&gt;
&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt; This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Your Extension===&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a specified position with a specified velocity.  We&#039;ll start with a very simple version, and add features until we have the extension you can find in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/AirStart.java&amp;lt;/code&amp;gt; with configurator in &amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our initial extension will simply start the rocket from a specified altitude.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        /** Launch altitude in meters */&lt;br /&gt;
        private static final double ALTITUDE = 1000.0;&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            Coordinate position = status.getRocketPosition();&lt;br /&gt;
            position = position.add(0, 0, ALTITUDE);&lt;br /&gt;
            status.setRocketPosition(position);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are two important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; method, which adds the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The listener itself.&lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartExampleProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartExampleProvider() {&lt;br /&gt;
        super(AirStartExample.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start example&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This method adds your extension to the extension menu.  The first string (&amp;lt;code&amp;gt;&amp;quot;Launch Conditions&amp;quot;&amp;lt;/code&amp;gt;) is the first level, while the second (&amp;lt;code&amp;gt;&amp;quot;Air-start example&amp;quot;&amp;lt;/code&amp;gt; is the actual menu entry.  These strings can be anything you want; using a first level entry that didn&#039;t previously exist will just add a new first level entry.&lt;br /&gt;
&lt;br /&gt;
Try it! Putting the extension in a file named &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExample.java&amp;lt;/code&amp;gt; and the provider in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extensions/example/AirStartExampleProvider.java&amp;lt;/code&amp;gt;, compiling, and running will give you a new entry in the extensions menu; adding it to the simulation will cause your simulation to start at an altitude of 1000 meters.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Flight Data===&lt;br /&gt;
&lt;br /&gt;
The results from executing a simulation is stored in a &lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35763</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35763"/>
		<updated>2022-12-11T04:46:44Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
===Simulation Status===&lt;br /&gt;
&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simulation Listeners===&lt;br /&gt;
&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt;/ This puts your extension into the menu described above.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Your Extension===&lt;br /&gt;
&lt;br /&gt;
To make things concrete, we&#039;ll start by creating a simple example extension, to air-start a rocket from a specified position with a specified velocity.  We&#039;ll start with a very simple version, and add features until we have the extension you can find in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/AirStart.java&amp;lt;/code&amp;gt; with configurator in &amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our initial extension will simply start the rocket from a specified altitude.&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
12&lt;br /&gt;
13&lt;br /&gt;
14&lt;br /&gt;
15&lt;br /&gt;
16&lt;br /&gt;
17&lt;br /&gt;
18&lt;br /&gt;
19&lt;br /&gt;
20&lt;br /&gt;
21&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
import net.sf.openrocket.simulation.SimulationStatus;&lt;br /&gt;
import net.sf.openrocket.simulation.exception.SimulationException;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;&lt;br /&gt;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;&lt;br /&gt;
import net.sf.openrocket.util.Coordinate;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Simulation extension that launches a rocket from a specific altitude.&lt;br /&gt;
 */&lt;br /&gt;
public class AirStartExample extends AbstractSimulationExtension {&lt;br /&gt;
&lt;br /&gt;
    public void initialize(SimulationConditions conditions) throws SimulationException {&lt;br /&gt;
        conditions.getSimulationListenerList().add(new AirStartListener());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class AirStartListener extends AbstractSimulationListener {&lt;br /&gt;
&lt;br /&gt;
        /** Launch altitude in meters */&lt;br /&gt;
        private static final double ALTITUDE = 1000.0;&lt;br /&gt;
&lt;br /&gt;
        @Override&lt;br /&gt;
        public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
            Coordinate position = status.getRocketPosition();&lt;br /&gt;
            position = position.add(0, 0, ALTITUDE);&lt;br /&gt;
            status.setRocketPosition(position);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are two important features in this example:&lt;br /&gt;
* The &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; method, which add the listener to the &amp;lt;code&amp;gt;List&amp;lt;/code&amp;gt; of simulation listeners.  This is the only method that is required to be defined in your extension.&lt;br /&gt;
* The listener itself.&lt;br /&gt;
&lt;br /&gt;
This will create the extension when it&#039;s compiled, but it won&#039;t put it in the simulation extension menu (so it&#039;ll be pretty much useless!).  To be able to actually use it, we need a provider, like this:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;top&amp;quot;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
 1&lt;br /&gt;
 2&lt;br /&gt;
 3&lt;br /&gt;
 4&lt;br /&gt;
 5&lt;br /&gt;
 6&lt;br /&gt;
 7&lt;br /&gt;
 8&lt;br /&gt;
 9&lt;br /&gt;
10&lt;br /&gt;
11&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
package net.sf.openrocket.simulation.extension.example;&lt;br /&gt;
&lt;br /&gt;
import net.sf.openrocket.plugin.Plugin;&lt;br /&gt;
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;&lt;br /&gt;
&lt;br /&gt;
@Plugin&lt;br /&gt;
public class AirStartProvider extends AbstractSimulationExtensionProvider {&lt;br /&gt;
    public AirStartProvider() {&lt;br /&gt;
        super(AirStart.class, &amp;quot;Launch conditions&amp;quot;, &amp;quot;Air-start&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Flight Data===&lt;br /&gt;
&lt;br /&gt;
The results from executing a simulation is stored in a &lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35762</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35762"/>
		<updated>2022-12-11T04:14:06Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;Edit simulation&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
===Simulation Statusa===&lt;br /&gt;
&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
You can obtain current information regarding the state of the simulation by calling &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; methods.  For instance, the rocket&#039;s current position is returned by calling &amp;lt;code&amp;gt;getRocketPosition()&amp;lt;/code; the rocket&#039;s position can be changed by calling &amp;lt;code&amp;gt;setRocketPosition&amp;lt;Coordinate position&amp;gt;&amp;lt;/code&amp;gt;.  All of the &amp;lt;code&amp;gt;get*()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;set*()&amp;lt;/code&amp;gt; methods can be found in &amp;lt;code&amp;gt;code/src/net/sf/openrocket/simulation/SimulationStatus.java&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simulation Listeners===&lt;br /&gt;
&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every listener receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;.  This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt;/ This puts your extension into the menu described above and calls the simulation.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===A Simple Example===&lt;br /&gt;
&lt;br /&gt;
To explore the process of writing an extension, we&#039;ll begin with a simple example: an extension that writes &amp;lt;code&amp;gt;Hello World&amp;lt;/code&amp;gt; to the standard output after every simulation step.  The extension is as follows:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Flight Data===&lt;br /&gt;
&lt;br /&gt;
The results from executing a simulation is stored in a &lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS)&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35761</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35761"/>
		<updated>2022-12-11T03:49:21Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;Edit simulation&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Creating a New OpenRocket Extension ==&lt;br /&gt;
&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
===Simulation Status and Flight Data===&lt;br /&gt;
&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the state object.&lt;br /&gt;
&lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Simulation Listeners===&lt;br /&gt;
&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every method receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;.  This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt;/ This puts your extension into the menu described above and calls the simulation.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===A Simple Example===&lt;br /&gt;
&lt;br /&gt;
To explore the process of writing an extension, we&#039;ll begin with a simple example: an extension that writes &amp;lt;code&amp;gt;Hello World&amp;lt;/code&amp;gt; to the standard output after every simulation step.  The extension is as follows:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35760</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35760"/>
		<updated>2022-12-10T01:20:24Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;Edit simulation&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Some Preliminary Concepts ==&lt;br /&gt;
&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
===Simulation Status and Flight Data===&lt;br /&gt;
&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the state object.&lt;br /&gt;
&lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Simulation Listeners===&lt;br /&gt;
&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every method receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;.  This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt;/ This puts your extension into the menu described above and calls the simulation.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===A Simple Example===&lt;br /&gt;
&lt;br /&gt;
To explore the process of writing an extension, we&#039;ll begin with a simple example: an extension that writes &amp;lt;code&amp;gt;Hello World&amp;lt;/code&amp;gt; to the standard output after every simulation step.  The extension is as follows:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
	<entry>
		<id>http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35759</id>
		<title>Simulation Extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.openrocket.info/index.php?title=Simulation_Extensions&amp;diff=35759"/>
		<updated>2022-12-09T18:53:51Z</updated>

		<summary type="html">&lt;p&gt;JoePfeiffer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;By using OpenRocket&#039;s extension and listener mechanism, it&#039;s possible to modify the program itself to add features that are not supported by the program as distributed; some extensions that have been created already provide the ability to air-start a rocket, to add active roll control, and to calculate and save extra flight data.&lt;br /&gt;
&lt;br /&gt;
This page will discuss extensions and simulations.  We&#039;ll start by showing how a simulation is executed (so you can get a taste of what&#039;s possible), and then document the process of creating the extension.  &#039;&#039;&#039;WARNING: writing an extension inserts new code into the program. It is entirely possible to disrupt a simulation in a way that invalidates simulation results, or can even crash the program.  Be careful!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Adding an Existing Extension to a Simulation ==&lt;br /&gt;
&lt;br /&gt;
Extensions are added to a simulation through a menu in the &amp;quot;Simulation Options&amp;quot; tab.&lt;br /&gt;
# Open a .ork file and go to the &#039;&#039;&#039;Flight Simulations&#039;&#039;&#039; tab&lt;br /&gt;
# Click the &#039;&#039;Edit simulation&#039;&#039; button to open the &#039;&#039;&#039;Edit simulation&#039;&#039;&#039; dialog.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; tab.&lt;br /&gt;
# Click the &#039;&#039;&#039;Add extension&#039;&#039;&#039; button&lt;br /&gt;
This will open a menu similar to the one in the following screenshot:&lt;br /&gt;
&lt;br /&gt;
[[File:Extension-menu.png]]&lt;br /&gt;
&lt;br /&gt;
Clicking on the name of an extension will add it to the simulation; if it has a configuration dialog the dialog will be opened:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-configuration.png]]&lt;br /&gt;
&lt;br /&gt;
In the case of the air-start extension, the configuration dialog allows you to set the altitude and velocity at which your simulation will begin.  After you close the configuration dialog (if any), a new panel will be added to the &#039;&#039;&#039;Simulation options&#039;&#039;&#039; pane, showing the new extension with buttons to reconfigure it, obtain information about it, or remove it from the simulation:&lt;br /&gt;
&lt;br /&gt;
[[File:Air-start-pane.png]]&lt;br /&gt;
&lt;br /&gt;
== Some Preliminary Concepts ==&lt;br /&gt;
&lt;br /&gt;
Before we can discuss writing an extension, we need to briefly discuss some of the internals of OpenRocket.  In particular,&lt;br /&gt;
we need to talk about the simulation status, flight data, and simulation listeners.&lt;br /&gt;
&lt;br /&gt;
===Simulation Status and Flight Data===&lt;br /&gt;
&lt;br /&gt;
As a simulation proceeds, it maintains its state in a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt;.  The &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; object contains information about the rocket&#039;s current position, orientation, velocity and simulation state.  It also contains a reference to a copy of the rocket design and its configuration.  Any simulation listener method may modify the state of the rocket by changing the properties of the state object.&lt;br /&gt;
&lt;br /&gt;
Assuming the simulation status is stored in &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;, we obtain the flight data for the currently executing simulation branch by calling &amp;lt;code&amp;gt;status.getFlightData()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
OpenRocket refers to simulation variables as &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s, which are &amp;lt;code&amp;gt;List&amp;amp;lt;Double&amp;amp;gt;&amp;lt;/code&amp;gt;s, with one list for each simulation variable.  To obtain a &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;, for example the current motor mass, from &amp;lt;code&amp;gt;flightData&amp;lt;/code&amp;gt;, we call &amp;lt;code&amp;gt;flightData.get(FlightDataType.TYPE_MOTOR_MASS))&amp;lt;/code&amp;gt;. The standard &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt;s are all created in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/FlightDataType.java&amp;lt;/code&amp;gt;; the mechanism for creating a new &amp;lt;code&amp;gt;FlightDataType&amp;lt;/code&amp;gt; for your extension will be described later.&lt;br /&gt;
&lt;br /&gt;
Data from the current simulation step can be obtained with e.g. &amp;lt;code&amp;gt;flightData.getLast(FlightDataType.TYPE_MOTOR_MASS&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Simulation Listeners===&lt;br /&gt;
&lt;br /&gt;
Simulation listeners are methods that OpenRocket calls at specified points in the computation to either record information or modify the simulation state.  These are divided into three interface classes, named &amp;lt;code&amp;gt;SimulationListener&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SimulationComputationListener&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SimulationEventListener&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All of these interfaces are implemented by the abstract class &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;. This class provides empty methods for all of the methods defined in the three interfaces, which are overridden as needed when writing a listener.  A typical listener method (which is actually in the Air-start listener), would be&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void startSimulation(SimulationStatus status) throws SimulationException {&lt;br /&gt;
    status.setRocketPosition(new Coordinate(0, 0, getLaunchAltitude()));&lt;br /&gt;
    status.setRocketVelocity(status.getRocketOrientationQuaternion().rotate(new Coordinate(0, 0, getLaunchVelocity())));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is called when the simulation is first started.  It obtains the desired launch altitude and velocity from its configuration, configuration, and inserts them into the simulation status.&lt;br /&gt;
&lt;br /&gt;
The full set of listener methods, with documentation regarding when they are called, can be found in &amp;lt;code&amp;gt;core/src/net/sf/openrocket/AbstractSimulationListener.java&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The listener methods can have three return value types:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;startSimulation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endSimulation&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;postStep&amp;lt;/code&amp;gt; are called at a specific point of the simulation.  They are void methods and do not return any value.&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;preStep&amp;lt;/code&amp;gt; and event-related hooks return a boolean value indicating whether the action should be taken or not.  A return value of &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; indicates that the action should be taken as normally would be (default), &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; will inhibit the action.&lt;br /&gt;
&lt;br /&gt;
* The pre- and post-computation methods may return the computed value, either as an object or a double value.  The pre-computation methods allow pre-empting the entire computation, while the post-computation methods allow augmenting the computed values.  These methods may return &amp;lt;code&amp;gt;null&amp;lt;/tt&amp;gt; or &amp;lt;code&amp;gt;Double.NaN&amp;lt;/code&amp;gt; to use the original values (default), or return an overriding value.&lt;br /&gt;
&lt;br /&gt;
Every method receives a &amp;lt;code&amp;gt;SimulationStatus&amp;lt;/code&amp;gt; (see above) object as the first argument, and may have additional arguments.&lt;br /&gt;
  &lt;br /&gt;
Each listener method may also throw a &amp;lt;code&amp;gt;SimulationException&amp;lt;/code&amp;gt;.  This is considered an error during simulation, and an error dialog is displayed to the user with the exception message.  The simulation data thus far is not stored in the simulation.  Throwing a &amp;lt;code&amp;gt;RuntimeException&amp;lt;/code&amp;gt; is considered a bug in the software and will result in a bug report dialog.&lt;br /&gt;
&lt;br /&gt;
If a simulation listener wants to stop a simulation prematurely without an error condition, it needs to add a flight event of type &amp;lt;code&amp;gt;FlightEvent.SIMULATION_END&amp;lt;/code&amp;gt; to the simulation event queue:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime(), null));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This will cause the simulation to be terminated normally.&lt;br /&gt;
&lt;br /&gt;
=== Creating a Simulation Extension ===&lt;br /&gt;
&lt;br /&gt;
Creating an extension for OpenRocket requires writing three classes:&lt;br /&gt;
&lt;br /&gt;
* A listener, which extends &amp;lt;code&amp;gt;AbstractSimulationListener&amp;lt;/code&amp;gt;.  This will be the bulk of your extension, and performs all the real work.&lt;br /&gt;
&lt;br /&gt;
* An extension, which extends &amp;lt;code&amp;gt;AbstractSimulationExtension&amp;lt;/code&amp;gt;. This inserts your listener into the simulation when it is called. Your listener can (and probably will) be private within your extension.&lt;br /&gt;
&lt;br /&gt;
* A provider, which extends &amp;lt;code&amp;gt;AbstractSimulationExtensionProvider&amp;lt;/code&amp;gt;/ This puts your extension into the menu described above and calls the simulation.&lt;br /&gt;
&lt;br /&gt;
In addition, if your extension will have a configuration GUI, you will need to write:&lt;br /&gt;
&lt;br /&gt;
* A configurator, which extends &amp;lt;code&amp;gt;AbstractSwingSimulationExtensionConfigurator&amp;amp;lt;E&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can either create your extension outside the source tree and insert it in OpenRocket&#039;s .jar file when compiled, or you can insert it in the source tree and compile it with OpenRocket.  Since all of OpenRocket&#039;s code is freely available, and reading the code for the existing extensions will be very helpful in writing your&#039;s, the easiest approach is to simply insert it in the source tree.  If you select this option, a very logical place to put your extension is in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;core/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the extensions provided with OpenRocket are defined.  Your configurator, if any, will logically go in&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;swing/src/net/sf/openrocket/simulation/extension/example/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===A Simple Example===&lt;br /&gt;
&lt;br /&gt;
To explore the process of writing an extension, we&#039;ll begin with a simple example: an extension that writes &amp;lt;code&amp;gt;Hello World&amp;lt;/code&amp;gt; to the standard output after every simulation step.  The extension is as follows:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>JoePfeiffer</name></author>
	</entry>
</feed>