Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
Attention: The newest changes should be on top -->

### Added

- ENH: Add save functionality to `_MonteCarloPlots.all` method [#848](https://github.com/RocketPy-Team/RocketPy/pull/848)
- ENH: Built-in flight comparison tool (`FlightComparator`) to validate simulations against external data [#888](https://github.com/RocketPy-Team/RocketPy/pull/888)
- ENH: Add persistent caching for ThrustCurve API [#881](https://github.com/RocketPy-Team/RocketPy/pull/881)
- ENH: Compatibility with MERRA-2 atmosphere reanalysis files [#825](https://github.com/RocketPy-Team/RocketPy/pull/825)
- ENH: Enable only radial burning [#815](https://github.com/RocketPy-Team/RocketPy/pull/815)
Expand Down
183 changes: 183 additions & 0 deletions docs/user/flight_comparator.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
Flight Comparator
=================

This example demonstrates how to use the RocketPy ``FlightComparator`` class to
compare a Flight simulation against external data sources.

Users must explicitly create a `FlightComparator` instance.


This class is designed to compare a RocketPy Flight simulation against external
data sources, such as:

- Real flight data (avionics logs, altimeter CSVs)
- Simulations from other software (OpenRocket, RASAero)
- Theoretical models or manual calculations

Unlike ``CompareFlights`` (which compares multiple RocketPy simulations),
``FlightComparator`` specifically handles the challenge of aligning different
time steps and calculating error metrics (RMSE, MAE, etc.) between a
"Reference" simulation and "External" data.

Importing classes
-----------------

We will start by importing the necessary classes and modules:

.. jupyter-execute::

import numpy as np

from rocketpy import Environment, Rocket, SolidMotor, Flight
from rocketpy.simulation import FlightComparator, FlightDataImporter

Create Simulation (Reference)
-----------------------------

First, let's create the standard RocketPy simulation that will serve as our
"Ground Truth" or reference model. This follows the standard setup.

.. jupyter-execute::

# 1. Setup Environment
env = Environment(
date=(2022, 10, 1, 12),
latitude=32.990254,
longitude=-106.974998,
elevation=1400,
)
env.set_atmospheric_model(type="standard_atmosphere")

# 2. Setup Motor
Pro75M1670 = SolidMotor(
thrust_source="../data/motors/cesaroni/Cesaroni_M1670.eng",
burn_time=3.9,
grain_number=5,
grain_density=1815,
grain_outer_radius=33 / 1000,
grain_initial_inner_radius=15 / 1000,
grain_initial_height=120 / 1000,
grain_separation=5 / 1000,
nozzle_radius=33 / 1000,
throat_radius=11 / 1000,
interpolation_method="linear",
coordinate_system_orientation="nozzle_to_combustion_chamber",
dry_mass=1.815,
dry_inertia=(0.125, 0.125, 0.002),
grains_center_of_mass_position=0.33,
center_of_dry_mass_position=0.317,
nozzle_position=0,
)

# 3. Setup Rocket
calisto = Rocket(
radius=127 / 2000,
mass=19.197 - 2.956,
inertia=(6.321, 6.321, 0.034),
power_off_drag="../data/calisto/powerOffDragCurve.csv",
power_on_drag="../data/calisto/powerOnDragCurve.csv",
center_of_mass_without_motor=0,
coordinate_system_orientation="tail_to_nose",
)

calisto.set_rail_buttons(0.0818, -0.618, 45)
calisto.add_motor(Pro75M1670, position=-1.255)

# Add aerodynamic surfaces
nosecone = calisto.add_nose(length=0.55829, kind="vonKarman", position=0.71971)
fin_set = calisto.add_trapezoidal_fins(
n=4,
root_chord=0.120,
tip_chord=0.040,
span=0.100,
position=-1.04956,
cant_angle=0.5,
airfoil=("../data/calisto/fins/NACA0012-radians.txt", "radians"),
)
tail = calisto.add_tail(
top_radius=0.0635,
bottom_radius=0.0435,
length=0.060,
position=-1.194656,
)

# 4. Simulate
flight = Flight(
rocket=calisto,
environment=env,
rail_length=5.2,
inclination=85,
heading=0,
)

# 5. Create FlightComparator instance
comparator = FlightComparator(flight)

Adding Another Flight Object
----------------------------

You can compare against another RocketPy Flight simulation directly:

.. jupyter-execute::

# Create a second simulation with slightly different parameters
flight2 = Flight(
rocket=calisto,
environment=env,
rail_length=5.0, # Slightly shorter rail
inclination=85,
heading=0,
)

# Add the second flight directly
comparator.add_data("Alternative Sim", flight2)

print(f"Added variables from flight2: {list(comparator.data_sources['Alternative Sim'].keys())}")

Importing External Data (dict)
------------------------------

The primary data format expected by ``FlightComparator.add_data`` is a dictionary
where keys are variable names (e.g. ``"z"``, ``"vz"``, ``"altitude"``) and values
are either:

- A RocketPy ``Function`` object, or
- A tuple of ``(time_array, data_array)``.

Let's create some synthetic external data to compare against our reference
simulation:

.. jupyter-execute::

# Generate synthetic external data with realistic noise
time_external = np.linspace(0, flight.t_final, 80) # Different time steps
external_altitude = flight.z(time_external) + np.random.normal(0, 3, 80) # 3m noise
external_velocity = flight.vz(time_external) + np.random.normal(0, 0.5, 80) # 0.5 m/s noise

# Add the external data to our comparator
comparator.add_data(
"External Simulator",
{
"altitude": (time_external, external_altitude),
"vz": (time_external, external_velocity),
}
)

Running Comparisons
-------------------

Now we can run the various comparison methods:

.. jupyter-execute::

# Generate summary with key events
comparator.summary()

# Compare specific variable
comparator.compare("altitude")

# Compare all available variables
comparator.all()

# Plot 2D trajectory
comparator.trajectories_2d(plane="xz")
36 changes: 18 additions & 18 deletions rocketpy/plots/compare/compare_flights.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def positions(
limit and second item, the y axis upper limit. If set to None, will
be calculated automatically by matplotlib.
legend : bool, optional
Weather or not to show the legend, by default True
Whether or not to show the legend, by default True
filename : str, optional
If a filename is provided, the plot will be saved to a file, by
default None. Image options are: png, pdf, ps, eps and svg.
Expand Down Expand Up @@ -196,7 +196,7 @@ def velocities(
limit and second item, the y axis upper limit. If set to None, will
be calculated automatically by matplotlib.
legend : bool, optional
Weather or not to show the legend, by default True
Whether or not to show the legend, by default True
filename : str, optional
If a filename is provided, the plot will be saved to a file, by
default None. Image options are: png, pdf, ps, eps and svg.
Expand Down Expand Up @@ -253,7 +253,7 @@ def stream_velocities(
limit and second item, the y axis upper limit. If set to None, will
be calculated automatically by matplotlib.
legend : bool, optional
Weather or not to show the legend, by default True
Whether or not to show the legend, by default True
filename : str, optional
If a filename is provided, the plot will be saved to a file, by
default None. Image options are: png, pdf, ps, eps and svg.
Expand Down Expand Up @@ -319,7 +319,7 @@ def accelerations(
limit and second item, the y axis upper limit. If set to None, will
be calculated automatically by matplotlib.
legend : bool, optional
Weather or not to show the legend, by default True
Whether or not to show the legend, by default True
filename : str, optional
If a filename is provided, the plot will be saved to a file, by
default None. Image options are: png, pdf, ps, eps and svg.
Expand Down Expand Up @@ -375,7 +375,7 @@ def euler_angles(
limit and second item, the y axis upper limit. If set to None, will
be calculated automatically by matplotlib.
legend : bool, optional
Weather or not to show the legend, by default True
Whether or not to show the legend, by default True
filename : str, optional
If a filename is provided, the plot will be saved to a file, by
default None. Image options are: png, pdf, ps, eps and svg.
Expand Down Expand Up @@ -435,7 +435,7 @@ def quaternions(
limit and second item, the y axis upper limit. If set to None, will
be calculated automatically by matplotlib.
legend : bool, optional
Weather or not to show the legend, by default True
Whether or not to show the legend, by default True
filename : str, optional
If a filename is provided, the plot will be saved to a file, by
default None. Image options are: png, pdf, ps, eps and svg.
Expand Down Expand Up @@ -491,7 +491,7 @@ def attitude_angles(
limit and second item, the y axis upper limit. If set to None, will
be calculated automatically by matplotlib.
legend : bool, optional
Weather or not to show the legend, by default True
Whether or not to show the legend, by default True
filename : str, optional
If a filename is provided, the plot will be saved to a file, by
default None. Image options are: png, pdf, ps, eps and svg.
Expand Down Expand Up @@ -546,7 +546,7 @@ def angular_velocities(
limit and second item, the y axis upper limit. If set to None, will
be calculated automatically by matplotlib.
legend : bool, optional
Weather or not to show the legend, by default True
Whether or not to show the legend, by default True
filename : str, optional
If a filename is provided, the plot will be saved to a file, by
default None. Image options are: png, pdf, ps, eps and svg.
Expand Down Expand Up @@ -601,7 +601,7 @@ def angular_accelerations(
limit and second item, the y axis upper limit. If set to None, will
be calculated automatically by matplotlib.
legend : bool, optional
Weather or not to show the legend, by default True
Whether or not to show the legend, by default True
filename : str, optional
If a filename is provided, the plot will be saved to a file, by
default None. Image options are: png, pdf, ps, eps and svg.
Expand Down Expand Up @@ -661,7 +661,7 @@ def aerodynamic_forces(
limit and second item, the y axis upper limit. If set to None, will
be calculated automatically by matplotlib.
legend : bool, optional
Weather or not to show the legend, by default True
Whether or not to show the legend, by default True
filename : str, optional
If a filename is provided, the plot will be saved to a file, by
default None. Image options are: png, pdf, ps, eps and svg.
Expand Down Expand Up @@ -720,7 +720,7 @@ def aerodynamic_moments(
limit and second item, the y axis upper limit. If set to None, will
be calculated automatically by matplotlib.
legend : bool, optional
Weather or not to show the legend, by default True
Whether or not to show the legend, by default True
filename : str, optional
If a filename is provided, the plot will be saved to a file,
by default None.
Expand Down Expand Up @@ -774,7 +774,7 @@ def energies(
limit and second item, the y axis upper limit. If set to None, will
be calculated automatically by matplotlib.
legend : bool, optional
Weather or not to show the legend, by default True
Whether or not to show the legend, by default True
filename : str, optional
If a filename is provided, the plot will be saved to a file,
by default None.
Expand Down Expand Up @@ -834,7 +834,7 @@ def powers(
limit and second item, the y axis upper limit. If set to None, will
be calculated automatically by matplotlib.
legend : bool, optional
Weather or not to show the legend, by default True
Whether or not to show the legend, by default True
filename : str, optional
If a filename is provided, the plot will be saved to a file,
by default None.
Expand Down Expand Up @@ -889,7 +889,7 @@ def rail_buttons_forces(
limit and second item, the y axis upper limit. If set to None, will
be calculated automatically by matplotlib.
legend : bool, optional
Weather or not to show the legend, by default True
Whether or not to show the legend, by default True
filename : str, optional
If a filename is provided, the plot will be saved to a file,
by default None.
Expand Down Expand Up @@ -955,7 +955,7 @@ def angles_of_attack(
limit and second item, the y axis upper limit. If set to None, will
be calculated automatically by matplotlib.
legend : bool, optional
Weather or not to show the legend, by default True
Whether or not to show the legend, by default True
filename : str, optional
If a filename is provided, the plot will be saved to a file,
by default None.
Expand Down Expand Up @@ -1011,7 +1011,7 @@ def fluid_mechanics(
limit and second item, the y axis upper limit. If set to None, will
be calculated automatically by matplotlib.
legend : bool, optional
Weather or not to show the legend, by default True
Whether or not to show the legend, by default True
filename : str, optional
If a filename is provided, the plot will be saved to a file,
by default None.
Expand Down Expand Up @@ -1074,7 +1074,7 @@ def stability_margin(
limit and second item, the y axis upper limit. If set to None, will
be calculated automatically by matplotlib.
legend : bool, optional
Weather or not to show the legend, by default True
Whether or not to show the legend, by default True
filename : str, optional
If a filename is provided, the plot will be saved to a file,
by default None.
Expand Down Expand Up @@ -1113,7 +1113,7 @@ def attitude_frequency(
limit and second item, the y axis upper limit. If set to None, will
be calculated automatically by matplotlib.
legend : bool, optional
Weather or not to show the legend, by default True
Whether or not to show the legend, by default True
filename : str, optional
If a filename is provided, the plot will be saved to a file,
by default None.
Expand Down
Loading