Skip to content

Commit 54cf332

Browse files
Merge pull request #1058 from RocketPy-Team/master
Updates develop with master
2 parents 5c17f5a + 4a67a41 commit 54cf332

28 files changed

Lines changed: 545 additions & 303 deletions

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ Attention: The newest changes should be on top -->
3232

3333
### Added
3434

35+
### Changed
36+
37+
### Fixed
38+
39+
40+
## [v1.13.0] - 2026-07-04
41+
42+
### Added
43+
3544
- ENH: ENH: Refactor flight.py latitude/longitude to use inverted_haversine [#1055](https://github.com/RocketPy-Team/RocketPy/pull/1055)
3645
- ENH: TST: Add unit tests for evaluate_reduced_mass and remove TODO [#1051](https://github.com/RocketPy-Team/RocketPy/pull/1051)
3746
- ENH: FIX: pre release hardening [#1047](https://github.com/RocketPy-Team/RocketPy/pull/1047)
@@ -60,6 +69,11 @@ Attention: The newest changes should be on top -->
6069

6170
### Changed
6271

72+
- REL: bumps up rocketpy version to 1.13.0 [#1048](https://github.com/RocketPy-Team/RocketPy/pull/1048)
73+
- MNT: **Breaking**: `Parachute` is now an abstract base class and can no longer be instantiated directly; use `HemisphericalParachute` (or `Rocket.add_parachute`, which builds it for you). Loading old serialized files still works [#958](https://github.com/RocketPy-Team/RocketPy/pull/958)
74+
- MNT: Discrete controllers are now called exactly once per time node (previously twice); results may change for stateful controllers and `observed_variables` no longer contains duplicated entries [#949](https://github.com/RocketPy-Team/RocketPy/pull/949)
75+
- MNT: Multi-dimensional linear `Function` objects now apply their extrapolation rule to points outside the data's convex hull (previously returned NaN) [#969](https://github.com/RocketPy-Team/RocketPy/pull/969)
76+
- MNT: Informational messages previously shown with `print()` now use Python logging and are silent by default; call `rocketpy.utils.enable_logging()` to see them [#973](https://github.com/RocketPy-Team/RocketPy/pull/973)
6377
- ENH: Refactor flight.py latitude/longitude to use inverted_haversine [#1055](https://github.com/RocketPy-Team/RocketPy/pull/1055)
6478

6579
### Deprecated
@@ -72,6 +86,8 @@ Attention: The newest changes should be on top -->
7286

7387
### Fixed
7488

89+
- BUG: fix individual fin (`TrapezoidalFin`, `EllipticalFin`, `FreeFormFin`) serialization crash when saving rockets/flights [#1048](https://github.com/RocketPy-Team/RocketPy/pull/1048)
90+
- BUG: support the new Wyoming sounding WSGI page format (legacy cgi-bin endpoint was discontinued by UWyo) [#1048](https://github.com/RocketPy-Team/RocketPy/pull/1048)
7591
- FIX: pre-release hardening — bug fixes across the unreleased features [#1047](https://github.com/RocketPy-Team/RocketPy/pull/1047)
7692
- BUG: Remove duplicate controller process; controllers were being invoked twice per time node [#949](https://github.com/RocketPy-Team/RocketPy/pull/949)
7793
- BUG: fix wind heading and direction wraparound interpolation [#974](https://github.com/RocketPy-Team/RocketPy/pull/974)

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ Here is just a quick taste of what RocketPy is able to calculate. There are hund
313313

314314
![6-DOF Trajectory Plot](https://raw.githubusercontent.com/RocketPy-Team/RocketPy/master/docs/static/rocketpy_example_trajectory.svg)
315315

316-
If you want to see the trajectory on Google Earth, RocketPy acn easily export a KML file for you:
316+
If you want to see the trajectory on Google Earth, RocketPy can easily export a KML file for you:
317317

318318
```python
319319
from rocketpy.simulation import FlightDataExporter

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
author = "RocketPy Team"
2828

2929
# The full version, including alpha/beta/rc tags
30-
release = "1.12.1"
30+
release = "1.13.0"
3131

3232

3333
# -- General configuration ---------------------------------------------------

docs/development/testing.rst

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,7 @@ Consider the following integration test:
257257
"""
258258
# TODO:: this should be added to the set_atmospheric_model() method as a
259259
# "file" option, instead of receiving the URL as a string.
260-
URL = "http://weather.uwyo.edu/cgi-bin/sounding?region=samer&TYPE=TEXT%3ALIST&YEAR=2019&MONTH=02&FROM=0500&TO=0512&STNM=83779"
261-
# give it at least 5 times to try to download the file
260+
URL = "https://weather.uwyo.edu/wsgi/sounding?datetime=2019-02-05+00:00:00&id=83779&type=TEXT:LIST"
262261
example_plain_env.set_atmospheric_model(type="wyoming_sounding", file=URL)
263262
264263
assert example_plain_env.all_info() is None
@@ -267,7 +266,7 @@ Consider the following integration test:
267266
abs(example_plain_env.barometric_height(example_plain_env.pressure(0)) - 722.0)
268267
< 1e-8
269268
)
270-
assert abs(example_plain_env.wind_velocity_x(0) - -2.9005178894925043) < 1e-8
269+
assert abs(example_plain_env.wind_velocity_x(0) - -2.9130471244363165) < 1e-8
271270
assert abs(example_plain_env.temperature(100) - 291.75) < 1e-8
272271
273272
This test contains two fundamental traits which defines it as an integration test:
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
Logging functions
22
-----------------
33

4-
.. automodule:: rocketpy.utils
5-
:members:
4+
.. autofunction:: rocketpy.utilities.enable_logging

docs/reference/classes/utils/utilities.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ At RocketPy projects, utilities are functions that:
1111
Below you have a list of all utilities functions available in rocketpy.
1212

1313
.. automodule:: rocketpy.utilities
14-
:members:
14+
:members:
15+
:exclude-members: enable_logging

docs/user/airbrakes.rst

Lines changed: 16 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -106,52 +106,12 @@ Defining the Controller Function
106106

107107
Lets start by defining a very simple controller function.
108108

109-
The ``controller_function`` must take in the following arguments, in this
110-
order:
111-
112-
1. ``time`` (float): The current simulation time in seconds.
113-
2. ``sampling_rate`` (float or ``None``): The rate at which the controller
114-
function is called, measured in Hertz (Hz). It is ``None`` for continuous
115-
controllers, so guard any ``1 / sampling_rate`` computation against ``None``.
116-
3. ``state`` (list): The state vector of the simulation. The state
117-
is a list containing the following values, in this order:
118-
119-
- ``x``: The x position of the rocket, in meters.
120-
- ``y``: The y position of the rocket, in meters.
121-
- ``z``: The z position of the rocket, in meters.
122-
- ``v_x``: The x component of the velocity of the rocket, in meters per
123-
second.
124-
- ``v_y``: The y component of the velocity of the rocket, in meters per
125-
second.
126-
- ``v_z``: The z component of the velocity of the rocket, in meters per
127-
second.
128-
- ``e0``: The first component of the quaternion representing the rotation
129-
of the rocket.
130-
- ``e1``: The second component of the quaternion representing the rotation
131-
of the rocket.
132-
- ``e2``: The third component of the quaternion representing the rotation
133-
of the rocket.
134-
- ``e3``: The fourth component of the quaternion representing the rotation
135-
of the rocket.
136-
- ``w_x``: The x component of the angular velocity of the rocket, in
137-
radians per second.
138-
- ``w_y``: The y component of the angular velocity of the rocket, in
139-
radians per second.
140-
- ``w_z``: The z component of the angular velocity of the rocket, in
141-
radians per second.
142-
143-
4. ``state_history`` (list): A record of the rocket's state at each
144-
step throughout the simulation. The state_history is organized as
145-
a list of lists, with each sublist containing a state vector. The
146-
last item in the list always corresponds to the previous state
147-
vector, providing a chronological sequence of the rocket's
148-
evolving states.
149-
5. ``observed_variables`` (list): A list containing the variables that
150-
the controller function returns. The return of each controller
151-
function call is appended to the observed_variables list. The
152-
initial value in the first step of the simulation of this list is
153-
provided by the ``initial_observed_variables`` argument.
154-
6. ``air_brakes`` (AirBrakes): The ``AirBrakes`` instance being controlled.
109+
The ``controller_function`` receives information about the simulation at the
110+
current time step and sets the air brakes' deployment level. See
111+
:ref:`controllers` for the full description of its arguments and call signature.
112+
For air brakes, the controlled object (the ``air_brakes`` argument) is the
113+
:class:`rocketpy.AirBrakes` instance, whose ``deployment_level`` the function
114+
sets.
155115

156116
Our example ``controller_function`` will deploy the air brakes when the rocket
157117
reaches 1500 meters above the ground. The deployment level will be function of the
@@ -227,22 +187,6 @@ Lets define the controller function:
227187

228188
.. note::
229189

230-
- The ``controller_function`` accepts 6, 7, or 8 parameters for backward
231-
compatibility:
232-
233-
* **6 parameters** (original): ``time``, ``sampling_rate``, ``state``,
234-
``state_history``, ``observed_variables``, ``air_brakes``
235-
* **7 parameters** (with sensors): adds ``sensors`` as the 7th parameter
236-
* **8 parameters** (with environment): adds ``sensors`` and ``environment``
237-
as the 7th and 8th parameters
238-
239-
- The **environment parameter** provides access to atmospheric conditions
240-
(wind, temperature, pressure, elevation) without relying on global variables.
241-
This enables proper serialization of rockets with air brakes and improves
242-
code modularity. Available methods include ``environment.elevation``,
243-
``environment.wind_velocity_x(altitude)``, ``environment.wind_velocity_y(altitude)``,
244-
``environment.speed_of_sound(altitude)``, and others.
245-
246190
- The code inside the ``controller_function`` can be as complex as needed.
247191
Anything can be implemented inside the function, including filters,
248192
apogee prediction, and any controller logic.
@@ -252,15 +196,10 @@ Lets define the controller function:
252196
0 or higher than 1. If you want to disable this feature, set ``clamp`` to
253197
``False`` when defining the air brakes.
254198

255-
- Anything can be returned by the ``controller_function``. The returned
256-
values will be saved in the ``observed_variables`` list at every time step
257-
and can then be accessed by the ``controller_function`` at the next time
258-
step. The saved values can also be accessed after the simulation is
259-
finished. This is useful for debugging and for plotting the results.
260-
261-
- The ``controller_function`` can also be defined in a separate file and
262-
imported into the simulation script. This includes importing a ``c`` or
263-
``cpp`` code into Python.
199+
- See :ref:`controllers` for the controller-function signature (including the
200+
6/7/8-parameter forms and the ``environment`` argument), how returned
201+
values are stored in ``observed_variables``, and discrete vs. continuous
202+
controllers.
264203

265204

266205
Defining the Drag Coefficient
@@ -375,40 +314,14 @@ controller function. If you want to disable this feature, set ``clamp`` to
375314
For more information on the :class:`rocketpy.AirBrakes` class
376315
initialization, see :class:`rocketpy.AirBrakes.__init__` section.
377316

378-
.. _discrete-vs-continuous-controllers:
379-
380-
Discrete vs. Continuous Controllers
381-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
382-
383-
The ``sampling_rate`` argument determines *when* the controller function is
384-
called during the flight simulation:
385-
386-
- **Discrete controller** (``sampling_rate`` set to a number, e.g. ``10``):
387-
the controller function is called at fixed intervals of ``1 / sampling_rate``
388-
seconds. This mirrors a real flight computer that reads its sensors and
389-
updates its actuators at a fixed frequency, and is the recommended choice
390-
when you want the simulation to reflect the actual control-loop rate of your
391-
hardware.
392-
- **Continuous controller** (``sampling_rate=None``): the controller function
393-
is called at *every* solver step of the numerical integrator. Use this when
394-
you want the control law to act as a continuous function of the state rather
395-
than a sampled one (for example, when validating a control model
396-
analytically).
397-
398-
.. warning::
399-
400-
For continuous controllers, ``sampling_rate`` is passed to your controller
401-
function as ``None``. Any computation such as ``1 / sampling_rate`` (a
402-
common pattern for rate-limiting deployment, as shown above) must guard
403-
against ``None`` to avoid a ``TypeError``.
404-
405317
.. note::
406318

407-
Discrete controllers add their sampling instants as time nodes to the
408-
simulation, so remember to set ``time_overshoot=False`` in the ``Flight``
409-
(see below) to make the integrator stop exactly at those instants.
410-
Continuous controllers do not add time nodes; they are evaluated on the
411-
integrator's own steps.
319+
Because our controller uses ``sampling_rate=10``, it is a **discrete**
320+
controller and adds its sampling instants as time nodes to the simulation.
321+
Remember to set ``time_overshoot=False`` in the ``Flight`` (see below) so the
322+
integrator stops exactly at those instants. See
323+
:ref:`discrete-vs-continuous-controllers` for the difference between discrete
324+
and continuous controllers.
412325

413326
Simulating a Flight
414327
-------------------

docs/user/controllers.rst

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
.. _controllers:
2+
3+
Controllers
4+
===========
5+
6+
RocketPy can simulate active, in-flight control systems — such as air brakes or
7+
other actuators — through a *controller function* that you attach to a
8+
controllable object (for example, an :class:`rocketpy.AirBrakes` added with
9+
``Rocket.add_air_brakes``). During the flight simulation, RocketPy calls your
10+
controller function at the appropriate times, letting it read the current
11+
simulation state and command the actuator.
12+
13+
This page describes the controller-function interface and how its call timing is
14+
governed. For a complete, runnable example that puts these pieces together, see
15+
the :doc:`Air Brakes Example <airbrakes>`.
16+
17+
The Controller Function
18+
-----------------------
19+
20+
A controller function receives information about the simulation up to the
21+
current time step and sets the state of the controlled object. It must take in
22+
the following arguments, in this order:
23+
24+
1. ``time`` (float): The current simulation time in seconds.
25+
2. ``sampling_rate`` (float or ``None``): The rate at which the controller
26+
function is called, measured in Hertz (Hz). It is ``None`` for continuous
27+
controllers, so guard any ``1 / sampling_rate`` computation against ``None``.
28+
3. ``state`` (list): The state vector of the simulation. The state
29+
is a list containing the following values, in this order:
30+
31+
- ``x``: The x position of the rocket, in meters.
32+
- ``y``: The y position of the rocket, in meters.
33+
- ``z``: The z position of the rocket, in meters.
34+
- ``v_x``: The x component of the velocity of the rocket, in meters per
35+
second.
36+
- ``v_y``: The y component of the velocity of the rocket, in meters per
37+
second.
38+
- ``v_z``: The z component of the velocity of the rocket, in meters per
39+
second.
40+
- ``e0``: The first component of the quaternion representing the rotation
41+
of the rocket.
42+
- ``e1``: The second component of the quaternion representing the rotation
43+
of the rocket.
44+
- ``e2``: The third component of the quaternion representing the rotation
45+
of the rocket.
46+
- ``e3``: The fourth component of the quaternion representing the rotation
47+
of the rocket.
48+
- ``w_x``: The x component of the angular velocity of the rocket, in
49+
radians per second.
50+
- ``w_y``: The y component of the angular velocity of the rocket, in
51+
radians per second.
52+
- ``w_z``: The z component of the angular velocity of the rocket, in
53+
radians per second.
54+
55+
4. ``state_history`` (list): A record of the rocket's state at each
56+
step throughout the simulation. The state_history is organized as
57+
a list of lists, with each sublist containing a state vector. The
58+
last item in the list always corresponds to the previous state
59+
vector, providing a chronological sequence of the rocket's
60+
evolving states.
61+
5. ``observed_variables`` (list): A list containing the variables that
62+
the controller function returns. The return of each controller
63+
function call is appended to the observed_variables list. The
64+
initial value in the first step of the simulation of this list is
65+
provided by the ``initial_observed_variables`` argument.
66+
6. The **controlled object** (e.g. ``air_brakes``): the instance being
67+
controlled, whose attributes the function sets (for example,
68+
``air_brakes.deployment_level``).
69+
70+
.. note::
71+
72+
- The controller function accepts 6, 7, or 8 parameters for backward
73+
compatibility:
74+
75+
* **6 parameters** (original): ``time``, ``sampling_rate``, ``state``,
76+
``state_history``, ``observed_variables``, and the controlled object.
77+
* **7 parameters** (with sensors): adds ``sensors`` as the 7th parameter.
78+
* **8 parameters** (with environment): adds ``sensors`` and ``environment``
79+
as the 7th and 8th parameters.
80+
81+
- The **environment parameter** provides access to atmospheric conditions
82+
(wind, temperature, pressure, elevation) without relying on global
83+
variables. This enables proper serialization of rockets with controllers
84+
and improves code modularity. Available methods include
85+
``environment.elevation``, ``environment.wind_velocity_x(altitude)``,
86+
``environment.wind_velocity_y(altitude)``,
87+
``environment.speed_of_sound(altitude)``, and others.
88+
89+
- Anything can be returned by the controller function. The returned values
90+
are saved in the ``observed_variables`` list at every time step and can
91+
then be accessed by the controller function at the next time step. The
92+
saved values can also be accessed after the simulation is finished, which
93+
is useful for debugging and for plotting the results.
94+
95+
- The controller function can also be defined in a separate file and
96+
imported into the simulation script. This includes importing ``c`` or
97+
``cpp`` code into Python.
98+
99+
.. _discrete-vs-continuous-controllers:
100+
101+
Discrete vs. Continuous Controllers
102+
-----------------------------------
103+
104+
The ``sampling_rate`` argument determines *when* the controller function is
105+
called during the flight simulation:
106+
107+
- **Discrete controller** (``sampling_rate`` set to a number, e.g. ``10``):
108+
the controller function is called at fixed intervals of ``1 / sampling_rate``
109+
seconds. This mirrors a real flight computer that reads its sensors and
110+
updates its actuators at a fixed frequency, and is the recommended choice
111+
when you want the simulation to reflect the actual control-loop rate of your
112+
hardware.
113+
- **Continuous controller** (``sampling_rate=None``): the controller function
114+
is called at *every* solver step of the numerical integrator. Use this when
115+
you want the control law to act as a continuous function of the state rather
116+
than a sampled one (for example, when validating a control model
117+
analytically).
118+
119+
.. warning::
120+
121+
For continuous controllers, ``sampling_rate`` is passed to your controller
122+
function as ``None``. Any computation such as ``1 / sampling_rate`` (a
123+
common pattern for rate-limiting deployment) must guard against ``None`` to
124+
avoid a ``TypeError``.
125+
126+
.. note::
127+
128+
Discrete controllers add their sampling instants as time nodes to the
129+
simulation, so remember to set ``time_overshoot=False`` in the ``Flight``
130+
to make the integrator stop exactly at those instants. Continuous
131+
controllers do not add time nodes; they are evaluated on the integrator's
132+
own steps.

0 commit comments

Comments
 (0)