Developer's Guide - openrocket/openrocket GitHub Wiki

This page contains information on the inner workings of OpenRocket and is meant primarily for developers interested in developing OpenRocket.

In addition to this page, information can be found in the Technical documentation chapter 5.

Obtaining the source code

OR is hosted on GitHub. You will need to clone the repository into your own workspace, using whatever git client is appropriate for your development environment (see below).

The sourcespy dashboard provides a high level overview of architecture, build process and dependencies of the project. It describes Ant tasks and dependencies, structure of UI classes, and other components of the system.

Development Environments

IntelliJ IDEA

At this writing (September 2023) most development is taking place using IntelliJ IDEA Community Edition

Command Line and Editors

A second development environment is a combination of the command line and a text editor.

Obtaining the source code using the command line

To obtain the source code from the command line, type

$ git clone https://github.com/openrocket/openrocket.git

OR also uses dbcook's component database repo (https://github.com/dbcook/openrocket-database) as a submodule. To initialize the submodule in your local OR repo, run the following commands from your OR root directory:

$ git submodule init
$ git submodule update

Note: The source packages available on the OpenRocket web site (*.zip) are meant for building the application from source code, NOT for development purposes. They do not contain all the project files etc.

Compilation

OR uses the ant build system. The most import build targets are

Command Purpose
$ ant Compiles any necessary source files, and builds the jar file
$ ant clean Deletes any artifacts of a previous compilation
$ ant unittest Runs the unit test suite

In addition, it is sometimes necessary to refresh OR's motor database from thrustcurve (this is only necessary when thrustcurve has been updated, or when debugging the thrust curve download code)

$ ant serialize-motors  

Execution

In order to execute OR from the command line, give the command

$ java -jar swing/build/jar/OpenRocket.jar

More command line options can be used to generate execution logs (see below).

Eclipse

At one time, Eclipse was the standard development environment for OR. While the following documentation is still provided for those who want to make use of it, in recent years our users have found setting up an Eclipse IDE for use with OR to be very challenging. We no longer recommend Eclipse as a development environment for OpenRocket. Use IntelliJ or the command line as described above.

To obtain the source code from within Eclipse, you will need install the EGit plugin to Eclipse.

Running OpenRocket from Eclipse (tested with Eclipse Luna and OpenRocket v15.03, September, 2015)

The following steps should get you up and running with OpenRocket source code:

  1. After downloading the source code, start Eclipse.
  2. From the Eclipse main menu, select File -> Import.
  3. In the Import window that opens, select General -> Existing Projects into Workspace, then click Next.
  4. In the Select root directory field, browse to the location of the OpenRocket source code that you downloaded.
  5. In the file browser that appears, click on the openrocket root project directory. A list of projects should then appear in the Projects pane on the import window.
  6. Select only these three projects from the list: OpenRocket Core, OpenRocket Swing and OpenRocket Test Libraries. (Note: As of v15.03 you no longer need to load the other projects to build OpenRocket on the desktop.)
  7. Once those three projects have been selected, click Finish. Eclipse should start with those three projects shown in the Package Explorer.

NOTE: You might see errors in some source files, reporting that various Java classes cannot be found. If that happens, you might try these steps to resolve these issues:

  1. In the Project Explorer pane in Eclipse, right-click on the project reporting the errors. Select Properties.
  2. In the properties window that opens, select Java Build Path -> Libraries.
  3. Make sure that the JRE System Library for your version of Java is seen in the list of libraries.
  4. If it is NOT included in the list, click the Add Library button in the menu to the right.
  5. From the Add Library window that appears, select JRE System Library from the list. Then click Next.
  6. Make sure that the correct Workspace is selected in the next window that appears, and then click Finish. Once the Add Library window closes, you should then see the JRE System Library added to the list of libraries in the project properties window.
  7. Click OK. Once the project properties window closes you will be returned to Eclipse, and hopefully the errors reported by the IDE will then resolve.
  8. If there are still errors, you'll need to revert to Google for additional help. Googling the exact error string reported by Eclipse will most likely help you resolve any outstanding dependency issues.

*** Also note that the main entry point for OpenRocket is in the OpenRocket Swing project, in the net.sf.openrocket.startup.SwingStartup.java file. With that file active OpenRocket should build and start.

Finally, remember that help is available either in the OpenRocket forums or in the main developers list. Good Luck!

Debugging

LogBack

OpenRocket uses LogBack for logging.

If you want to change the default logging that OpenRocket uses, you can create or reuse a LogBack configuration file stored in openrocket/core/config. For example, you can use the logback-stdout-level-error.xml there which tells logback to log everything to stdout with logging level of Error (serious errors only).

To enable use of a LogBack configuration file, pass the JVM the following option during startup:

-Dlogback.configurationFile=config/logback-stdout-level-error.xml

See the LogBack configuration page for more details.

Log Levels

Log messages in OR are specified by one of six levels. You can specify which level of errors you want reported to standard out with a system property which you can add to your VM argument, e.g:

-Dopenrocket.log.stdout=debug

The error levels available are:

ERROR - Level for indicating a bug or error condition noticed in the software or JRE. No ERROR level events should occur while running the program.

WARN - Level for indicating error conditions or atypical events that can occur during normal operation (errors while loading files, weird computation results etc).

USER - Level for logging user actions (adding and modifying components, running simulations etc). A user action should be logged as soon as possible on this level. The level is separate so that additional INFO messages won't purge user actions from a bounded log buffer.

INFO - Level for indicating general level actions the software is performing and other notable events during execution (dialogs shown, simulations run etc)

DEBUG - Level for indicating mid-results, outcomes of methods and other debugging information. The data logged should be of value when analyzing error conditions and what has caused them. Places that are called repeatedly during e.g. flight simulation should use the VBOSE level instead.

VBOSE - Level of verbose debug logging to be used in areas which are called repeatedly, such as computational methods used in simulations. This level is separated to allow filtering out the verbose logs generated during simulations, DnD etc. from the normal debug logs.

Writing to the Log

In the code, the standard way to enter a log entry is with

log.verbose("Message");

where log is defined in each class as :

private static final Logger log = LoggerFactory.getLogger(classname.class);

The logging methods, and their correspondence to the log levels, are

Method Level
log.error() ERROR
log.warn() WARN
log.info() INFO
log.debug() DEBUG
log.trace() VBOSE

Yes, the list of logging levels above includes a USER level which does not appear in this table. I can't find any use of the expected log.user() call in the code

Contributing Code to OpenRocket

Developing OpenRocket is a group effort, and with any group effort that doesn't want to end up in confusion, inefficiency and errors, you need management and some good ol' GitHub etiquette.

Whenever a user encounters a problem, he/she/they should post it as an issue. If you as a developer want to work on an issue, you should first communicate that you want to work on that issue. This can be done by commenting on the issue something like 'I would like to work on this issue'. This ensures that no more than one person works on a given issue. See, less confusion and more efficiency!

Before doing any programming, you need to prepare your git-environment on your computer. First you need to fork the official OpenRocket-repository to your account as described above. If done correctly, you should see the forked OpenRocket-repository when you go to your GitHub-account. Now you have to clone your own OpenRocket-repository to your computer. Now you have your very own version of the OpenRocket-repo on your computer in which you can play around.

Next, you need to create a new branch of the repo that corresponds to the issue that you wish to solve. Give that branch a meaningful name, for example a branch for fixing issue #123 should be called 'issue-123'. Creating the branch could thus be made through command line (make sure that you're in the 'openrocket'-directory):

$ git checkout -b issue-123

This command automatically creates branch 'issue-123' and switches to that new branch.

Now you can unleash your wildest dreams onto the code.

GitHub etiquette 1: make use of atomic commits. This means: don't fix 10 different issues and cram them in one commit. Split up commits into smaller commits that fix only one issue/feature. For example: I fixed an issue where a button would be displayed as red instead of blue, but I also found that there was a typo in a text somewhere else. Then put the button-fix in a separate commit, give it an appropriate name, and put the typo-fix in another commit. Atomic commits make it much easier for code reviewers to review the code changes.

GitHub etiquette 2: good naming convention of a commit is in the form of '[#] '. Take the example of fixing the red button from issue #123: '[#123] Display red button as blue'. Mentioning '#123' will also automatically link your pull request to the corresponding issue.

Right, you've dug into the codebase, found that one nasty line that caused all your troubles and fixed it. It is now time to push your code and create a pull request of the branch from your own repository to the official repository. As your PR (Pull Request) text, it is good to have the following structure:

  1. Explain briefly which issue that you are trying to solve, e.g. 'This PR solves #123 in which buttons were displayed as red instead of blue'
  2. Next explain what the underlying issue was, e.g. 'The problem was that by default Java swing displays buttons as red.'
  3. Next is how you fixed the issue, e.g. 'Fixed it by overriding the default button color to blue'
  4. Finally, for other people to test your code, it is good to include a jar file of your fixed OpenRocket. This can be done using ant (more info here). You can upload this jar-file to e.g. Dropbox or Google Drive and include it in your PR, e.g. 'Here is a jar file for testing: ' or if you're really GitHub-savvy, you can add a hyperlink to the 'jar file'-text. If necessary, you can also include information on how to recreate the original issue so that testers can check whether your code solved the issue. If needed, you can also included information about the expected behavior so that others know what your solution should do.

An example PR is #979.

Units used in OpenRocket

OpenRocket always uses internally pure SI units. For example all rocket dimensions and flight distances are in meters, all masses are in kilograms, density is in kg/m³, temperature is in Kelvin etc. This convention is also used when storing the design in the OpenRocket format.

The only exception to this rule is angles:

  • Angles are represented as radians internally, but in the file format they are converted to degrees. This is to make the file format more human-readable and to avoid rounding errors.
  • Latitude and longitude of the launch site are represented in degrees both internally and externally.

When displaying measures to the user, the values are converted into the preferred units of the user. This is performed using classes in the package net.sf.openrocket.unit. The Unit class represents a single unit and it includes methods for converting between that unit and SI units in addition to creating a string representation with a suitable amount of decimals. A UnitGroup describes a measurable quantity such as temperature and contains the units available for that quantity, such as Celcius, Fahrenheit and Kelvin.

Rocket design loading and saving

(This section is based on email correspondence)

All file reading/writing is performed in the package net.sf.openrocket.file and subpackages. To implement a new document loader it is necessary to extend the class RocketLoader and implement the abstract method loadFromStream(InputStream). This method simply loads the document and returns an OpenRocketDocument object.

An OpenRocketDocument contains all the information about an opened document, primarily the rocket structure and the simulations. The rocket structure is a simple tree-like structure of RocketComponents. All of the components are in the package .rocketcomponent. A diagram of their hierarchy and a short explanation of each class is available in my thesis section 5.1 Documentation

I've implemented a simple XML reading framework (based on SAX), which simplifies reading by assuming that XML elements can contain only other elements or text content, but not both. Both the OpenRocket and Rocksim format abide with this restriction.

The framework is in the package .file.simplesax. The primary class that will be extended is ElementHandler, which contains three methods. openElement() is called when a new subelement is found, and it returns a new ElementHandler to handle that element (which can be the object itself), or null in order to ignore that element and all of its contents (for example for unknown elements). closeElement() is called when the subelement ends, and endHandler() is called when the element of the current handler ends. The JavaDoc should provide the necessary details.

There are a few ready handlers, the most useful of which will probably be PlainTextHandler. This handler accepts only text content and ignores all subelements. If the XML file contains value, then the handler can return a PlainTextHandler and handle the content within the closeElement() method, removing the need to write a specialized handler for that element.

The XML reading is initiated by calling SimpleSAX.readXML() with the input source and the initial ElementHandler.

The OpenRocket document loading is implemented in .file.openrocket.OpenRocketLoader. Here I've used a bit more boilerplate code to be able to define most of the rocket structure preferences without needing separate handlers for them.

The OpenRocket format (*.ork)

The OpenRocket native format uses the file extension *.ork. It is an XML format file combined with any needed graphics files, contained in a Zip archive.

Currently the file format is not documented other than as the reference implementation, and no XML schema exists. This will hopefully change in the future. See Units Used in OpenRocket for details on units.

Every time the XML format changes, the file version number is increased. This version number is not linked to the version of OpenRocket that created the file, but instead describes the file format. Later versions of OpenRocket should attempt to store files in the oldest format that supports all the necessary features. The changes in the format are described in the fileformat.txt file in Git.

Updating motor database

OR uses Thrustcurves.org for its thrustcurves/motors. Fetching this data is as of the writing of this documentation done manually using ant. To update the thrustcurves, simply run the following terminal command from the 'core' directory:

$ ant serialize-motors

This will fetch all the data and cache it inside 'core/resources/datafiles/thrustcurves/thrustcurves.ser'. You can then simply make a pull request of the thrustcurves.ser-file to implement the changes into OR.

Adding new libraries

Libraries are located in a dedicated lib directory in the Core and Swing module. To add a new library, just put the JAR of the library you want to include in the lib-folder of the module that you want to use the library in. E.g. if I have a JAR-file 'commonmark-0.18.1.jar' for the commonmark-java library that I would like to use in a Java class in the Core module, then put the JAR-file in /core/lib.

If you use IntelliJ as an IDE, you can also generate the JAR-file if you only have the Maven-code of your library. Just go to File > Project Structure, then under Modules select the module that you want the library to be in, hit the '+'-button, 'Library > From Maven', look up 'commonmark', select the right maven dependency in the dropdown menu and then check 'Download to' and as file path direct to your local core/lib or swing/lib folder.

Now you still need to link the JAR library to the project. Add the classpathentry in the core/.classpath or swing/.classpath. E.g. for the commonmark library in the core module, add this line to core/.classpath: . Also add this dependency to swing/.classpath with .

If you use IntelliJ and did not add the library using the Project Structure as mentioned above, also include the necessary info in 'core/OpenRocket Core.iml'.

Finally, we still need to update swing/build.xml to include the library for when we build the project into a JAR. For the commonmark-library, include the line .

Note: using externalized lib-folders with JAR-file libraries is an archaic way of library management. If you would like to help in porting OR's libraries to Maven or Gradle, be my guest!

⚠️ **GitHub.com Fallback** ⚠️