Skip to content

Flight summaries

Helper.get_summary turns a simulated flight into one plain-Python record — the numbers every flight report needs, without hand-rolling timeseries post-processing:

import orlab

with orlab.OpenRocketInstance() as instance:
    orl = orlab.Helper(instance)
    doc = orl.load_doc("rocket.ork")
    sim = doc.getSimulation(0)
    orl.run_simulation(sim)

    summary = orl.get_summary(sim)
    print(summary)
    print(summary.apogee, summary.max_velocity, summary.descent_rate)

print(summary) renders a sectioned report (rail departure, ascent, recovery & descent, landing, warnings). Every field is a builtin float, int, or str in SI units (FlightSummary.UNITS maps fields to units) — never a Java or numpy type — so summaries pickle cleanly into other processes and serialize without surprises.

Dispersion tables

to_dict() makes a summary one table row; a study is a list of rows:

summaries = []
for i in range(100):
    randomize(sim)          # your parameter dispersion
    orl.run_simulation(sim)
    summaries.append(orl.get_summary(sim))

rows = [s.to_dict() for s in summaries]

import pandas as pd  # optional — plain dicts work with csv.DictWriter too
table = pd.DataFrame(rows)
print(table[["apogee", "landing_distance", "landing_bearing_deg"]].describe())

examples/simple_ork/monte_carlo.py is this pattern end to end.

Timeseries tables

The full per-sample record — not just the scalar summary — exports in one call. export_csv is stdlib-only; get_dataframe needs the orlab[pandas] extra (pip install 'orlab[pandas]'; everything else in orlab works without pandas):

orl.export_csv(sim, "flight.csv")   # UTF-8, "NAME (SI unit)" headers
frame = orl.get_dataframe(sim)      # same columns as a pandas DataFrame
print(frame[["TYPE_TIME (s)", "TYPE_ALTITUDE (m)"]].tail())

By default every profile data type populated on the branch is included, TYPE_TIME first; pass variables=[...] (enum members or constant-name strings) to select and order columns yourself. Column labels carry the jar's own SI units (TYPE_ACCELERATION_TOTAL (m/s²)); dimensionless quantities like TYPE_MACH_NUMBER get no suffix. NaN samples become empty CSV cells, which pandas.read_csv reads back as NaN — the round trip is lossless. Simulation warnings are not per-sample data and have no column: take them from get_summary(sim).warnings.

Field semantics worth knowing

  • Missing means NaN, never None. A booster branch has no launch-rod departure, pre-23.09 OpenRocket computes no optimum_delay, a flight without a recovery event has no descent_rate. When a field is NaN because the loaded OpenRocket version lacks the underlying data (not because of the flight itself), orlab logs one warning per process — so version drift can't silently corrupt dispersion statistics.
  • Stability is windowed. min_stability_cal/max_stability_cal cover launch-rod departure → apogee. OpenRocket 24.12 computes stability through post-apogee tumble, where unwindowed minima (−9 cal and worse) are meaningless.
  • time_to_apogee on branch 0 is OpenRocket's own figure; the APOGEE event time in get_events can differ by a sample step.
  • Landing is flat-earth: landing_x (east), landing_y (north), distance and compass bearing derived from them. Geodetic coordinates are deliberately not summarized (their stored units changed across OpenRocket versions).
  • Pickles are transport, not archive. Load them with the same orlab version that wrote them; for storage, use to_dict()/CSV.

Multi-stage flights

Each stage branch gets its own summary; branch_count tells you how many:

for branch in range(orl.get_summary(sim).branch_count):
    print(orl.get_summary(sim, branch_number=branch))

Booster branches report NaN for rail-departure and recovery fields they don't have; their apogee/max_velocity come from the branch's own data.

Drogue vs main descent

descent_rate averages from the last recovery deployment to ground hit (the main, in a dual-deploy flight). For per-device rates, window the timeseries yourself with the event times:

import numpy as np

events = orl.get_events(sim)  # get_events(sim, branch_number=...) for stages
drogue_t, main_t = events[orlab.FlightEvent.RECOVERY_DEVICE_DEPLOYMENT][:2]
data = orl.get_timeseries(
    sim, [orlab.FlightDataType.TYPE_TIME, orlab.FlightDataType.TYPE_VELOCITY_Z]
)
t = data[orlab.FlightDataType.TYPE_TIME]
vz = data[orlab.FlightDataType.TYPE_VELOCITY_Z]
window = (t >= drogue_t) & (t <= main_t) & ~np.isnan(vz)
print("drogue descent rate:", -vz[window].mean())