Skip to content

Commit 47e5b61

Browse files
hogatataGui-FernandesBR
authored andcommitted
add axial_accelaration and test.
1 parent 02c1827 commit 47e5b61

File tree

2 files changed

+289
-53
lines changed

2 files changed

+289
-53
lines changed

rocketpy/simulation/flight.py

Lines changed: 258 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# pylint: disable=too-many-lines
2+
import json
23
import math
34
import warnings
45
from copy import deepcopy
56
from functools import cached_property
67

78
import numpy as np
9+
import simplekml
810
from scipy.integrate import BDF, DOP853, LSODA, RK23, RK45, OdeSolver, Radau
911

10-
from rocketpy.simulation.flight_data_exporter import FlightDataExporter
11-
1212
from ..mathutils.function import Function, funcify_method
1313
from ..mathutils.vector_matrix import Matrix, Vector
1414
from ..motors.point_mass_motor import PointMassMotor
@@ -17,7 +17,6 @@
1717
from ..rocket import PointMassRocket
1818
from ..tools import (
1919
calculate_cubic_hermite_coefficients,
20-
deprecated,
2120
euler313_to_quaternions,
2221
find_closest,
2322
find_root_linear_interpolation,
@@ -1136,7 +1135,6 @@ def __init_solution_monitors(self):
11361135
self.out_of_rail_time_index = 0
11371136
self.out_of_rail_state = np.array([0])
11381137
self.apogee_state = np.array([0])
1139-
self.apogee = 0
11401138
self.apogee_time = 0
11411139
self.x_impact = 0
11421140
self.y_impact = 0
@@ -2619,6 +2617,15 @@ def max_acceleration(self):
26192617
"""Maximum acceleration reached by the rocket."""
26202618
max_acceleration_time_index = np.argmax(self.acceleration[:, 1])
26212619
return self.acceleration[max_acceleration_time_index, 1]
2620+
2621+
@cached_property
2622+
def axial_acceleration(self):
2623+
"""Axial acceleration magnitude as a function of time."""
2624+
return (
2625+
self.ax * self.attitude_vector_x
2626+
+ self.ay * self.attitude_vector_y
2627+
+ self.az * self.attitude_vector_z
2628+
)
26222629

26232630
@funcify_method("Time (s)", "Horizontal Speed (m/s)")
26242631
def horizontal_speed(self):
@@ -3415,72 +3422,270 @@ def calculate_stall_wind_velocity(self, stall_angle): # TODO: move to utilities
34153422
+ f" of attack exceeds {stall_angle:.3f}°: {w_v:.3f} m/s"
34163423
)
34173424

3418-
@deprecated(
3419-
reason="Moved to FlightDataExporter.export_pressures()",
3420-
version="v1.12.0",
3421-
alternative="rocketpy.simulation.flight_data_exporter.FlightDataExporter.export_pressures",
3422-
)
3423-
def export_pressures(self, file_name, time_step):
3424-
"""
3425-
.. deprecated:: 1.11
3426-
Use :class:`rocketpy.simulation.flight_data_exporter.FlightDataExporter`
3427-
and call ``.export_pressures(...)``.
3425+
def export_pressures(self, file_name, time_step): # TODO: move out
3426+
"""Exports the pressure experienced by the rocket during the flight to
3427+
an external file, the '.csv' format is recommended, as the columns will
3428+
be separated by commas. It can handle flights with or without
3429+
parachutes, although it is not possible to get a noisy pressure signal
3430+
if no parachute is added.
3431+
3432+
If a parachute is added, the file will contain 3 columns: time in
3433+
seconds, clean pressure in Pascals and noisy pressure in Pascals.
3434+
For flights without parachutes, the third column will be discarded
3435+
3436+
This function was created especially for the 'Projeto Jupiter'
3437+
Electronics Subsystems team and aims to help in configuring
3438+
micro-controllers.
3439+
3440+
Parameters
3441+
----------
3442+
file_name : string
3443+
The final file name,
3444+
time_step : float
3445+
Time step desired for the final file
3446+
3447+
Return
3448+
------
3449+
None
34283450
"""
3429-
return FlightDataExporter(self).export_pressures(file_name, time_step)
3451+
time_points = np.arange(0, self.t_final, time_step)
3452+
# pylint: disable=W1514, E1121
3453+
with open(file_name, "w") as file:
3454+
if len(self.rocket.parachutes) == 0:
3455+
print("No parachutes in the rocket, saving static pressure.")
3456+
for t in time_points:
3457+
file.write(f"{t:f}, {self.pressure.get_value_opt(t):.5f}\n")
3458+
else:
3459+
for parachute in self.rocket.parachutes:
3460+
for t in time_points:
3461+
p_cl = parachute.clean_pressure_signal_function.get_value_opt(t)
3462+
p_ns = parachute.noisy_pressure_signal_function.get_value_opt(t)
3463+
file.write(f"{t:f}, {p_cl:.5f}, {p_ns:.5f}\n")
3464+
# We need to save only 1 parachute data
3465+
break
34303466

3431-
@deprecated(
3432-
reason="Moved to FlightDataExporter.export_data()",
3433-
version="v1.12.0",
3434-
alternative="rocketpy.simulation.flight_data_exporter.FlightDataExporter.export_data",
3435-
)
34363467
def export_data(self, file_name, *variables, time_step=None):
3468+
"""Exports flight data to a comma separated value file (.csv).
3469+
3470+
Data is exported in columns, with the first column representing time
3471+
steps. The first line of the file is a header line, specifying the
3472+
meaning of each column and its units.
3473+
3474+
Parameters
3475+
----------
3476+
file_name : string
3477+
The file name or path of the exported file. Example: flight_data.csv
3478+
Do not use forbidden characters, such as / in Linux/Unix and
3479+
`<, >, :, ", /, \\, | ?, *` in Windows.
3480+
variables : strings, optional
3481+
Names of the data variables which shall be exported. Must be Flight
3482+
class attributes which are instances of the Function class. Usage
3483+
example: test_flight.export_data('test.csv', 'z', 'angle_of_attack',
3484+
'mach_number').
3485+
time_step : float, optional
3486+
Time step desired for the data. If None, all integration time steps
3487+
will be exported. Otherwise, linear interpolation is carried out to
3488+
calculate values at the desired time steps. Example: 0.001.
34373489
"""
3438-
.. deprecated:: 1.11
3439-
Use :class:`rocketpy.simulation.flight_data_exporter.FlightDataExporter`
3440-
and call ``.export_data(...)``.
3441-
"""
3442-
return FlightDataExporter(self).export_data(
3443-
file_name, *variables, time_step=time_step
3490+
# TODO: we should move this method to outside of class.
3491+
3492+
# Fast evaluation for the most basic scenario
3493+
if time_step is None and len(variables) == 0:
3494+
np.savetxt(
3495+
file_name,
3496+
self.solution,
3497+
fmt="%.6f",
3498+
delimiter=",",
3499+
header=""
3500+
"Time (s),"
3501+
"X (m),"
3502+
"Y (m),"
3503+
"Z (m),"
3504+
"E0,"
3505+
"E1,"
3506+
"E2,"
3507+
"E3,"
3508+
"W1 (rad/s),"
3509+
"W2 (rad/s),"
3510+
"W3 (rad/s)",
3511+
)
3512+
return
3513+
3514+
# Not so fast evaluation for general case
3515+
if variables is None:
3516+
variables = [
3517+
"x",
3518+
"y",
3519+
"z",
3520+
"vx",
3521+
"vy",
3522+
"vz",
3523+
"e0",
3524+
"e1",
3525+
"e2",
3526+
"e3",
3527+
"w1",
3528+
"w2",
3529+
"w3",
3530+
]
3531+
3532+
if time_step is None:
3533+
time_points = self.time
3534+
else:
3535+
time_points = np.arange(self.t_initial, self.t_final, time_step)
3536+
3537+
exported_matrix = [time_points]
3538+
exported_header = "Time (s)"
3539+
3540+
# Loop through variables, get points and names (for the header)
3541+
for variable in variables:
3542+
if variable in self.__dict__:
3543+
variable_function = self.__dict__[variable]
3544+
# Deal with decorated Flight methods
3545+
else:
3546+
try:
3547+
obj = getattr(self.__class__, variable)
3548+
variable_function = obj.__get__(self, self.__class__)
3549+
except AttributeError as exc:
3550+
raise AttributeError(
3551+
f"Variable '{variable}' not found in Flight class"
3552+
) from exc
3553+
variable_points = variable_function(time_points)
3554+
exported_matrix += [variable_points]
3555+
exported_header += f", {variable_function.__outputs__[0]}"
3556+
3557+
exported_matrix = np.array(exported_matrix).T # Fix matrix orientation
3558+
3559+
np.savetxt(
3560+
file_name,
3561+
exported_matrix,
3562+
fmt="%.6f",
3563+
delimiter=",",
3564+
header=exported_header,
3565+
encoding="utf-8",
34443566
)
34453567

3446-
@deprecated(
3447-
reason="Moved to FlightDataExporter.export_sensor_data()",
3448-
version="v1.12.0",
3449-
alternative="rocketpy.simulation.flight_data_exporter.FlightDataExporter.export_sensor_data",
3450-
)
34513568
def export_sensor_data(self, file_name, sensor=None):
3569+
"""Exports sensors data to a file. The file format can be either .csv or
3570+
.json.
3571+
3572+
Parameters
3573+
----------
3574+
file_name : str
3575+
The file name or path of the exported file. Example: flight_data.csv
3576+
Do not use forbidden characters, such as / in Linux/Unix and
3577+
`<, >, :, ", /, \\, | ?, *` in Windows.
3578+
sensor : Sensor, string, optional
3579+
The sensor to export data from. Can be given as a Sensor object or
3580+
as a string with the sensor name. If None, all sensors data will be
3581+
exported. Default is None.
34523582
"""
3453-
.. deprecated:: 1.11
3454-
Use :class:`rocketpy.simulation.flight_data_exporter.FlightDataExporter`
3455-
and call ``.export_sensor_data(...)``.
3456-
"""
3457-
return FlightDataExporter(self).export_sensor_data(file_name, sensor=sensor)
3583+
if sensor is None:
3584+
data_dict = {}
3585+
for used_sensor, measured_data in self.sensor_data.items():
3586+
data_dict[used_sensor.name] = measured_data
3587+
else:
3588+
# export data of only that sensor
3589+
data_dict = {}
3590+
3591+
if not isinstance(sensor, str):
3592+
data_dict[sensor.name] = self.sensor_data[sensor]
3593+
else: # sensor is a string
3594+
matching_sensors = [s for s in self.sensor_data if s.name == sensor]
3595+
3596+
if len(matching_sensors) > 1:
3597+
data_dict[sensor] = []
3598+
for s in matching_sensors:
3599+
data_dict[s.name].append(self.sensor_data[s])
3600+
elif len(matching_sensors) == 1:
3601+
data_dict[sensor] = self.sensor_data[matching_sensors[0]]
3602+
else:
3603+
raise ValueError("Sensor not found in the Flight.sensor_data.")
34583604

3459-
@deprecated(
3460-
reason="Moved to FlightDataExporter.export_kml()",
3461-
version="v1.12.0",
3462-
alternative="rocketpy.simulation.flight_data_exporter.FlightDataExporter.export_kml",
3463-
)
3464-
def export_kml(
3605+
with open(file_name, "w") as file:
3606+
json.dump(data_dict, file)
3607+
print("Sensor data exported to: ", file_name)
3608+
3609+
def export_kml( # TODO: should be moved out of this class.
34653610
self,
34663611
file_name="trajectory.kml",
34673612
time_step=None,
34683613
extrude=True,
34693614
color="641400F0",
34703615
altitude_mode="absolute",
34713616
):
3617+
"""Exports flight data to a .kml file, which can be opened with Google
3618+
Earth to display the rocket's trajectory.
3619+
3620+
Parameters
3621+
----------
3622+
file_name : string
3623+
The file name or path of the exported file. Example: flight_data.csv
3624+
time_step : float, optional
3625+
Time step desired for the data. If None, all integration time steps
3626+
will be exported. Otherwise, linear interpolation is carried out to
3627+
calculate values at the desired time steps. Example: 0.001.
3628+
extrude: bool, optional
3629+
To be used if you want to project the path over ground by using an
3630+
extruded polygon. In case False only the linestring containing the
3631+
flight path will be created. Default is True.
3632+
color : str, optional
3633+
Color of your trajectory path, need to be used in specific kml
3634+
format. Refer to http://www.zonums.com/gmaps/kml_color/ for more
3635+
info.
3636+
altitude_mode: str
3637+
Select elevation values format to be used on the kml file. Use
3638+
'relativetoground' if you want use Above Ground Level elevation, or
3639+
'absolute' if you want to parse elevation using Above Sea Level.
3640+
Default is 'relativetoground'. Only works properly if the ground
3641+
level is flat. Change to 'absolute' if the terrain is to irregular
3642+
or contains mountains.
34723643
"""
3473-
.. deprecated:: 1.11
3474-
Use :class:`rocketpy.simulation.flight_data_exporter.FlightDataExporter`
3475-
and call ``.export_kml(...)``.
3476-
"""
3477-
return FlightDataExporter(self).export_kml(
3478-
file_name=file_name,
3479-
time_step=time_step,
3480-
extrude=extrude,
3481-
color=color,
3482-
altitude_mode=altitude_mode,
3483-
)
3644+
# Define time points vector
3645+
if time_step is None:
3646+
time_points = self.time
3647+
else:
3648+
time_points = np.arange(self.t_initial, self.t_final + time_step, time_step)
3649+
# Open kml file with simplekml library
3650+
kml = simplekml.Kml(open=1)
3651+
trajectory = kml.newlinestring(name="Rocket Trajectory - Powered by RocketPy")
3652+
3653+
if altitude_mode == "relativetoground":
3654+
# In this mode the elevation data will be the Above Ground Level
3655+
# elevation. Only works properly if the ground level is similar to
3656+
# a plane, i.e. it might not work well if the terrain has mountains
3657+
coords = [
3658+
(
3659+
self.longitude.get_value_opt(t),
3660+
self.latitude.get_value_opt(t),
3661+
self.altitude.get_value_opt(t),
3662+
)
3663+
for t in time_points
3664+
]
3665+
trajectory.coords = coords
3666+
trajectory.altitudemode = simplekml.AltitudeMode.relativetoground
3667+
else: # altitude_mode == 'absolute'
3668+
# In this case the elevation data will be the Above Sea Level elevation
3669+
# Ensure you use the correct value on self.env.elevation, otherwise
3670+
# the trajectory path can be offset from ground
3671+
coords = [
3672+
(
3673+
self.longitude.get_value_opt(t),
3674+
self.latitude.get_value_opt(t),
3675+
self.z.get_value_opt(t),
3676+
)
3677+
for t in time_points
3678+
]
3679+
trajectory.coords = coords
3680+
trajectory.altitudemode = simplekml.AltitudeMode.absolute
3681+
# Modify style of trajectory linestring
3682+
trajectory.style.linestyle.color = color
3683+
trajectory.style.polystyle.color = color
3684+
if extrude:
3685+
trajectory.extrude = 1
3686+
# Save the KML
3687+
kml.save(file_name)
3688+
print("File ", file_name, " saved with success!")
34843689

34853690
def info(self):
34863691
"""Prints out a summary of the data available about the Flight."""

0 commit comments

Comments
 (0)