|
1 | 1 | import numpy as np |
2 | 2 | import pytest |
3 | 3 |
|
4 | | -from rocketpy import Rocket |
| 4 | +from rocketpy import LinearGenericSurface, Rocket |
| 5 | + |
| 6 | +# TODO: review note: gotta test execution speed of changes in this branch |
| 7 | + |
| 8 | +def _linear_surface_from_barrowman(surface): |
| 9 | + """Build a LinearGenericSurface that reproduces a Barrowman surface's aero. |
| 10 | +
|
| 11 | + Reads the coefficient curves off a standard (Barrowman) aerodynamic surface |
| 12 | + -- its normal-force-curve slope ``clalpha`` as a function of Mach and, for fin |
| 13 | + sets, its roll cant and damping coefficients -- and packs them into the |
| 14 | + body-frame coefficient derivatives of an equivalent |
| 15 | + :class:`LinearGenericSurface`. The pitch- and yaw-plane slopes follow the |
| 16 | + Barrowman sign convention (``cN_alpha = clalpha`` and ``cY_beta = -clalpha``). |
| 17 | +
|
| 18 | + The returned surface applies its force at its own origin (center of pressure |
| 19 | + ``(0, 0, 0)``); the caller is expected to add it at the surface's center of |
| 20 | + pressure station (``barrowman_station - clalpha_cp``) so that its force |
| 21 | + lands, and its static margin reads, at the same point as the Barrowman |
| 22 | + surface. Its ``cpz`` (the distance from the surface origin to its center of |
| 23 | + pressure) is exposed on the returned object as ``barrowman_cpz`` to make that |
| 24 | + offset easy for the caller. |
| 25 | +
|
| 26 | + Parameters |
| 27 | + ---------- |
| 28 | + surface : rocketpy.NoseCone, rocketpy.Tail or rocketpy fin set |
| 29 | + A standard Barrowman aerodynamic surface to copy the aero curves from. |
| 30 | +
|
| 31 | + Returns |
| 32 | + ------- |
| 33 | + rocketpy.LinearGenericSurface |
| 34 | + A linear generic surface with the same normal force and (for fins) roll |
| 35 | + behaviour as ``surface``, carrying the source surface's ``cpz`` as |
| 36 | + ``barrowman_cpz``. |
| 37 | + """ |
| 38 | + clalpha = surface.clalpha # normal-force-curve slope, a Function of Mach |
| 39 | + coefficients = { |
| 40 | + "cN_alpha": clalpha, |
| 41 | + "cY_beta": lambda mach: -clalpha.get_value_opt(mach), |
| 42 | + } |
| 43 | + # Fin sets carry roll coefficients: cant forcing (zero when uncanted) and |
| 44 | + # roll-rate damping. Other surfaces have no roll_parameters. |
| 45 | + if getattr(surface, "roll_parameters", None) is not None: |
| 46 | + coefficients["cl_0"] = surface.cl_0 |
| 47 | + coefficients["cl_p"] = surface.cl_p |
| 48 | + linear_surface = LinearGenericSurface( |
| 49 | + reference_area=surface.reference_area, |
| 50 | + reference_length=surface.reference_length, |
| 51 | + coefficients=coefficients, |
| 52 | + center_of_pressure=(0, 0, 0), |
| 53 | + name=f"{surface.name}_linear", |
| 54 | + ) |
| 55 | + linear_surface.barrowman_cpz = surface.cpz |
| 56 | + return linear_surface |
5 | 57 |
|
6 | 58 |
|
7 | 59 | @pytest.fixture |
@@ -184,6 +236,89 @@ def calisto_robust( |
184 | 236 | return calisto |
185 | 237 |
|
186 | 238 |
|
| 239 | +@pytest.fixture |
| 240 | +def calisto_linear_generic( |
| 241 | + cesaroni_m1670, |
| 242 | + calisto_nose_cone, |
| 243 | + calisto_tail, |
| 244 | + calisto_trapezoidal_fins, |
| 245 | + calisto_main_chute, |
| 246 | + calisto_drogue_chute, |
| 247 | +): |
| 248 | + """Calisto built entirely from LinearGenericSurfaces instead of Barrowman ones. |
| 249 | +
|
| 250 | + This is the same rocket as ``calisto_robust`` -- same body, motor, rail |
| 251 | + buttons and parachutes at the same stations -- but its nose cone, tail and |
| 252 | + fin set are each replaced by a body-frame ``LinearGenericSurface``. The |
| 253 | + coefficient curves of those linear surfaces are extracted from the matching |
| 254 | + standard (Barrowman) surfaces: each linear surface reuses the standard |
| 255 | + surface's normal-force-curve slope, center of pressure and (for the fins) |
| 256 | + roll damping. Because the aero data is identical, this rocket's flight |
| 257 | + closely tracks the standard Calisto's while exercising the linear |
| 258 | + generic-surface aerodynamic path -- the one whose forces and moments are |
| 259 | + built directly in the body frame from the coefficient derivatives, with no |
| 260 | + wind-to-body rotation. It is a standalone rocket (it does not reuse the |
| 261 | + shared ``calisto`` fixture), so a test may build both it and ``calisto_robust`` |
| 262 | + and compare their flights. |
| 263 | +
|
| 264 | + Parameters |
| 265 | + ---------- |
| 266 | + cesaroni_m1670 : rocketpy.SolidMotor |
| 267 | + The Calisto motor. This is a pytest fixture too. |
| 268 | + calisto_nose_cone : rocketpy.NoseCone |
| 269 | + The standard nose cone whose aero curves are copied. This is a pytest |
| 270 | + fixture too. |
| 271 | + calisto_tail : rocketpy.Tail |
| 272 | + The standard boat tail whose aero curves are copied. This is a pytest |
| 273 | + fixture too. |
| 274 | + calisto_trapezoidal_fins : rocketpy.TrapezoidalFins |
| 275 | + The standard fin set whose aero curves are copied. This is a pytest |
| 276 | + fixture too. |
| 277 | + calisto_main_chute : rocketpy.Parachute |
| 278 | + The main parachute of the Calisto rocket. This is a pytest fixture too. |
| 279 | + calisto_drogue_chute : rocketpy.Parachute |
| 280 | + The drogue parachute of the Calisto rocket. This is a pytest fixture too. |
| 281 | +
|
| 282 | + Returns |
| 283 | + ------- |
| 284 | + rocketpy.Rocket |
| 285 | + The Calisto rocket whose nose cone, tail and fins are all |
| 286 | + LinearGenericSurfaces. |
| 287 | + """ |
| 288 | + calisto = Rocket( |
| 289 | + radius=0.0635, |
| 290 | + mass=14.426, |
| 291 | + inertia=(6.321, 6.321, 0.034), |
| 292 | + power_off_drag="data/rockets/calisto/powerOffDragCurve.csv", |
| 293 | + power_on_drag="data/rockets/calisto/powerOnDragCurve.csv", |
| 294 | + center_of_mass_without_motor=0, |
| 295 | + coordinate_system_orientation="tail_to_nose", |
| 296 | + ) |
| 297 | + calisto.add_motor(cesaroni_m1670, position=-1.373) |
| 298 | + # Replace each Barrowman surface with an equivalent LinearGenericSurface. A |
| 299 | + # Barrowman surface is placed by its origin and carries its center of |
| 300 | + # pressure at ``cpz`` aft of that origin; a generic surface applies its force |
| 301 | + # at its own origin. So each linear surface is added at the Barrowman |
| 302 | + # surface's center-of-pressure station (station - cpz, with tail_to_nose |
| 303 | + # csys = +1), which lands its force -- and its static margin -- at the same |
| 304 | + # point as calisto_robust. |
| 305 | + for surface, station in ( |
| 306 | + (calisto_nose_cone, 1.160), |
| 307 | + (calisto_tail, -1.313), |
| 308 | + (calisto_trapezoidal_fins, -1.168), |
| 309 | + ): |
| 310 | + linear_surface = _linear_surface_from_barrowman(surface) |
| 311 | + calisto.add_surfaces(linear_surface, station - linear_surface.barrowman_cpz) |
| 312 | + calisto.set_rail_buttons( |
| 313 | + upper_button_position=0.082, |
| 314 | + lower_button_position=-0.618, |
| 315 | + angular_position=0, |
| 316 | + ) |
| 317 | + calisto.parachutes.append(calisto_main_chute) |
| 318 | + calisto.parachutes.append(calisto_drogue_chute) |
| 319 | + return calisto |
| 320 | + |
| 321 | + |
187 | 322 | @pytest.fixture |
188 | 323 | def calisto_nose_to_tail_robust( |
189 | 324 | calisto_nose_to_tail, |
|
0 commit comments