diff --git a/CHANGELOG.md b/CHANGELOG.md index ee3c84968..90e4911f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,7 +32,6 @@ Attention: The newest changes should be on top --> ### Added -- BUG: Fix StochasticFlight time_overshoot None bug [#805] (https://github.com/RocketPy-Team/RocketPy/pull/805) - ENH: Implement Multivariate Rejection Sampling (MRS) [#738] (https://github.com/RocketPy-Team/RocketPy/pull/738) - ENH: Create a rocketpy file to store flight simulations [#800](https://github.com/RocketPy-Team/RocketPy/pull/800) - ENH: Support for the RSE file format has been added to the library [#798](https://github.com/RocketPy-Team/RocketPy/pull/798) @@ -43,9 +42,11 @@ Attention: The newest changes should be on top --> ### Fixed +- BUG: Unecessary Gyroscope Rotation and Wrong Acceleremoter Rotation [#811](https://github.com/RocketPy-Team/RocketPy/pull/811) - BUG: Fix the handling of reference pressure for older rpy files. [#808](https://github.com/RocketPy-Team/RocketPy/pull/808) - BUG: Non-overshootable simulations error on time parsing. [#807](https://github.com/RocketPy-Team/RocketPy/pull/807) - BUG: Wrong Phi Initialization For nose_to_tail Rockets [#809](https://github.com/RocketPy-Team/RocketPy/pull/809) +- BUG: Fix StochasticFlight time_overshoot None bug [#805](https://github.com/RocketPy-Team/RocketPy/pull/805) ## v1.9.0 - 2025-03-24 diff --git a/rocketpy/prints/sensors_prints.py b/rocketpy/prints/sensors_prints.py index 4bf844c5a..4e65651df 100644 --- a/rocketpy/prints/sensors_prints.py +++ b/rocketpy/prints/sensors_prints.py @@ -74,7 +74,7 @@ def orientation(self): self._print_aligned("Orientation:", self.sensor.orientation) self._print_aligned("Normal Vector:", self.sensor.normal_vector) print("Rotation Matrix:") - for row in self.sensor.rotation_matrix: + for row in self.sensor.rotation_sensor_to_body: value = " ".join(f"{val:.2f}" for val in row) value = [float(val) for val in value.split()] self._print_aligned("", value) diff --git a/rocketpy/sensors/accelerometer.py b/rocketpy/sensors/accelerometer.py index 935f4e3cf..de249c173 100644 --- a/rocketpy/sensors/accelerometer.py +++ b/rocketpy/sensors/accelerometer.py @@ -44,7 +44,7 @@ class Accelerometer(InertialSensor): The cross axis sensitivity of the sensor in percentage. name : str The name of the sensor. - rotation_matrix : Matrix + rotation_sensor_to_body : Matrix The rotation matrix of the sensor from the sensor frame to the rocket frame of reference. normal_vector : Vector @@ -241,8 +241,9 @@ def measure(self, time, **kwargs): + Vector.cross(omega, Vector.cross(omega, r)) ) # Transform to sensor frame - inertial_to_sensor = self._total_rotation_matrix @ Matrix.transformation( - u[6:10] + inertial_to_sensor = ( + self._total_rotation_sensor_to_body + @ Matrix.transformation(u[6:10]).transpose ) A = inertial_to_sensor @ A diff --git a/rocketpy/sensors/gyroscope.py b/rocketpy/sensors/gyroscope.py index cafac1cdc..5acbb6f50 100644 --- a/rocketpy/sensors/gyroscope.py +++ b/rocketpy/sensors/gyroscope.py @@ -1,6 +1,6 @@ import numpy as np -from ..mathutils.vector_matrix import Matrix, Vector +from ..mathutils.vector_matrix import Vector from ..prints.sensors_prints import _GyroscopePrints from ..sensors.sensor import InertialSensor @@ -44,7 +44,7 @@ class Gyroscope(InertialSensor): The cross axis sensitivity of the sensor in percentage. name : str The name of the sensor. - rotation_matrix : Matrix + rotation_sensor_to_body : Matrix The rotation matrix of the sensor from the sensor frame to the rocket frame of reference. normal_vector : Vector @@ -222,14 +222,11 @@ def measure(self, time, **kwargs): u_dot = kwargs["u_dot"] relative_position = kwargs["relative_position"] - # Angular velocity of the rocket in the rocket frame + # Angular velocity of the rocket in the body frame omega = Vector(u[10:13]) # Transform to sensor frame - inertial_to_sensor = self._total_rotation_matrix @ Matrix.transformation( - u[6:10] - ) - W = inertial_to_sensor @ omega + W = self._total_rotation_sensor_to_body @ omega # Apply noise + bias and quantize W = self.apply_noise(W) @@ -237,18 +234,14 @@ def measure(self, time, **kwargs): # Apply acceleration sensitivity if self.acceleration_sensitivity != Vector.zeros(): - W += self.apply_acceleration_sensitivity( - omega, u_dot, relative_position, inertial_to_sensor - ) + W += self.apply_acceleration_sensitivity(omega, u_dot, relative_position) W = self.quantize(W) self.measurement = tuple([*W]) self._save_data((time, *W)) - def apply_acceleration_sensitivity( - self, omega, u_dot, relative_position, rotation_matrix - ): + def apply_acceleration_sensitivity(self, omega, u_dot, relative_position): """ Apply acceleration sensitivity to the sensor measurement @@ -260,8 +253,6 @@ def apply_acceleration_sensitivity( The time derivative of the state vector relative_position : Vector The vector from the rocket's center of mass to the sensor - rotation_matrix : Matrix - The rotation matrix from the rocket frame to the sensor frame Returns ------- @@ -271,7 +262,7 @@ def apply_acceleration_sensitivity( # Linear acceleration of rocket cdm in inertial frame inertial_acceleration = Vector(u_dot[3:6]) - # Angular velocity and accel of rocket + # Angular accel of rocket in body frame omega_dot = Vector(u_dot[10:13]) # Acceleration felt in sensor @@ -281,7 +272,7 @@ def apply_acceleration_sensitivity( + Vector.cross(omega, Vector.cross(omega, relative_position)) ) # Transform to sensor frame - A = rotation_matrix @ A + A = self._total_rotation_sensor_to_body @ A return self.acceleration_sensitivity & A diff --git a/rocketpy/sensors/sensor.py b/rocketpy/sensors/sensor.py index 91dab8e45..416201019 100644 --- a/rocketpy/sensors/sensor.py +++ b/rocketpy/sensors/sensor.py @@ -460,23 +460,23 @@ def __init__( # rotation matrix and normal vector if any(isinstance(row, (tuple, list)) for row in orientation): # matrix - self.rotation_matrix = Matrix(orientation) + self.rotation_sensor_to_body = Matrix(orientation) elif len(orientation) == 3: # euler angles - self.rotation_matrix = Matrix.transformation_euler_angles( + self.rotation_sensor_to_body = Matrix.transformation_euler_angles( *np.deg2rad(orientation) ).round(12) else: raise ValueError("Invalid orientation format") self.normal_vector = Vector( [ - self.rotation_matrix[0][2], - self.rotation_matrix[1][2], - self.rotation_matrix[2][2], + self.rotation_sensor_to_body[0][2], + self.rotation_sensor_to_body[1][2], + self.rotation_sensor_to_body[2][2], ] ).unit_vector # cross axis sensitivity matrix - _cross_axis_matrix = 0.01 * Matrix( + cross_axis_matrix = 0.01 * Matrix( [ [100, self.cross_axis_sensitivity, self.cross_axis_sensitivity], [self.cross_axis_sensitivity, 100, self.cross_axis_sensitivity], @@ -485,7 +485,9 @@ def __init__( ) # compute total rotation matrix given cross axis sensitivity - self._total_rotation_matrix = self.rotation_matrix @ _cross_axis_matrix + self._total_rotation_sensor_to_body = ( + self.rotation_sensor_to_body @ cross_axis_matrix + ) def _vectorize_input(self, value, name): if isinstance(value, (int, float)): diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index 94e97fee4..ce728dafe 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -104,11 +104,14 @@ class Flight: Flight.z : Function Rocket's z coordinate (positive up) as a function of time. Flight.vx : Function - Rocket's X velocity as a function of time. + Velocity of the rocket's center of dry mass in the X (East) direction of + the inertial frame as a function of time. Flight.vy : Function - Rocket's Y velocity as a function of time. + Velocity of the rocket's center of dry mass in the Y (North) direction of + the inertial frame as a function of time. Flight.vz : Function - Rocket's Z velocity as a function of time. + Velocity of the rocket's center of dry mass in the Z (Up) direction of + the inertial frame as a function of time. Flight.e0 : Function Rocket's Euler parameter 0 as a function of time. Flight.e1 : Function @@ -118,17 +121,17 @@ class Flight: Flight.e3 : Function Rocket's Euler parameter 3 as a function of time. Flight.w1 : Function - Rocket's angular velocity Omega 1 as a function of time. - Direction 1 is in the rocket's body axis and points perpendicular - to the rocket's axis of cylindrical symmetry. + Angular velocity of the rocket in the x direction of the rocket's + body frame as a function of time, in rad/s. Sometimes referred to as + pitch rate (q). Flight.w2 : Function - Rocket's angular velocity Omega 2 as a function of time. - Direction 2 is in the rocket's body axis and points perpendicular - to the rocket's axis of cylindrical symmetry and direction 1. + Angular velocity of the rocket in the y direction of the rocket's + body frame as a function of time, in rad/s. Sometimes referred to as + yaw rate (r). Flight.w3 : Function - Rocket's angular velocity Omega 3 as a function of time. - Direction 3 is in the rocket's body axis and points in the - direction of cylindrical symmetry. + Angular velocity of the rocket in the z direction of the rocket's + body frame as a function of time, in rad/s. Sometimes referred to as + roll rate (p). Flight.latitude: Function Rocket's latitude coordinates (positive North) as a function of time. The Equator has a latitude equal to 0, by convention. @@ -209,49 +212,46 @@ class Flight: Stores and manages flight phases. Flight.wind_velocity_x : Function Wind velocity X (East) experienced by the rocket as a - function of time. Can be called or accessed as array. + function of time. Flight.wind_velocity_y : Function Wind velocity Y (North) experienced by the rocket as a - function of time. Can be called or accessed as array. + function of time. Flight.density : Function Air density experienced by the rocket as a function of - time. Can be called or accessed as array. + time. Flight.pressure : Function Air pressure experienced by the rocket as a function of - time. Can be called or accessed as array. + time. Flight.dynamic_viscosity : Function Air dynamic viscosity experienced by the rocket as a function of - time. Can be called or accessed as array. + time. Flight.speed_of_sound : Function Speed of Sound in air experienced by the rocket as a - function of time. Can be called or accessed as array. + function of time. Flight.ax : Function - Rocket's X (East) acceleration as a function of time, in m/s². - Can be called or accessed as array. + Acceleration of the rocket's center of dry mass along the X (East) + axis in the inertial frame as a function of time. Flight.ay : Function - Rocket's Y (North) acceleration as a function of time, in m/s². - Can be called or accessed as array. + Acceleration of the rocket's center of dry mass along the Y (North) + axis in the inertial frame as a function of time. Flight.az : Function - Rocket's Z (Up) acceleration as a function of time, in m/s². - Can be called or accessed as array. + Acceleration of the rocket's center of dry mass along the Z (Up) + axis in the inertial frame as a function of time. Flight.alpha1 : Function - Rocket's angular acceleration Alpha 1 as a function of time. - Direction 1 is in the rocket's body axis and points perpendicular - to the rocket's axis of cylindrical symmetry. - Units of rad/s². Can be called or accessed as array. + Angular acceleration of the rocket in the x direction of the rocket's + body frame as a function of time, in rad/s. Sometimes referred to as + yaw acceleration. Flight.alpha2 : Function - Rocket's angular acceleration Alpha 2 as a function of time. - Direction 2 is in the rocket's body axis and points perpendicular - to the rocket's axis of cylindrical symmetry and direction 1. - Units of rad/s². Can be called or accessed as array. + Angular acceleration of the rocket in the y direction of the rocket's + body frame as a function of time, in rad/s. Sometimes referred to as + yaw acceleration. Flight.alpha3 : Function - Rocket's angular acceleration Alpha 3 as a function of time. - Direction 3 is in the rocket's body axis and points in the - direction of cylindrical symmetry. - Units of rad/s². Can be called or accessed as array. + Angular acceleration of the rocket in the z direction of the rocket's + body frame as a function of time, in rad/s. Sometimes referred to as + roll acceleration. Flight.speed : Function Rocket velocity magnitude in m/s relative to ground as a - function of time. Can be called or accessed as array. + function of time. Flight.max_speed : float Maximum velocity magnitude in m/s reached by the rocket relative to ground during flight. @@ -260,11 +260,10 @@ class Flight: magnitude relative to ground. Flight.horizontal_speed : Function Rocket's velocity magnitude in the horizontal (North-East) - plane in m/s as a function of time. Can be called or - accessed as array. + plane in m/s as a function of time. Flight.acceleration : Function Rocket acceleration magnitude in m/s² relative to ground as a - function of time. Can be called or accessed as array. + function of time. Flight.max_acceleration : float Maximum acceleration magnitude in m/s² reached by the rocket relative to ground during flight. @@ -275,94 +274,65 @@ class Flight: Rocket's flight path angle, or the angle that the rocket's velocity makes with the horizontal (North-East) plane. Measured in degrees and expressed as a function - of time. Can be called or accessed as array. + of time. Flight.attitude_vector_x : Function Rocket's attitude vector, or the vector that points in the rocket's axis of symmetry, component in the X direction (East) as a function of time. - Can be called or accessed as array. Flight.attitude_vector_y : Function Rocket's attitude vector, or the vector that points in the rocket's axis of symmetry, component in the Y direction (East) as a function of time. - Can be called or accessed as array. Flight.attitude_vector_z : Function Rocket's attitude vector, or the vector that points in the rocket's axis of symmetry, component in the Z direction (East) as a function of time. - Can be called or accessed as array. Flight.attitude_angle : Function Rocket's attitude angle, or the angle that the rocket's axis of symmetry makes with the horizontal (North-East) plane. Measured in degrees and expressed as a function - of time. Can be called or accessed as array. + of time. Flight.lateral_attitude_angle : Function Rocket's lateral attitude angle, or the angle that the rocket's axis of symmetry makes with plane defined by the launch rail direction and the Z (up) axis. Measured in degrees and expressed as a function - of time. Can be called or accessed as array. + of time. Flight.phi : Function Rocket's Spin Euler Angle, φ, according to the 3-2-3 rotation system nomenclature (NASA Standard Aerospace). Measured in degrees and - expressed as a function of time. Can be called or accessed as array. + expressed as a function of time. Flight.theta : Function Rocket's Nutation Euler Angle, θ, according to the 3-2-3 rotation system nomenclature (NASA Standard Aerospace). Measured in degrees and - expressed as a function of time. Can be called or accessed as array. + expressed as a function of time. Flight.psi : Function Rocket's Precession Euler Angle, ψ, according to the 3-2-3 rotation system nomenclature (NASA Standard Aerospace). Measured in degrees and - expressed as a function of time. Can be called or accessed as array. + expressed as a function of time. Flight.R1 : Function - Resultant force perpendicular to rockets axis due to - aerodynamic forces as a function of time. Units in N. - Expressed as a function of time. Can be called or accessed - as array. - Direction 1 is in the rocket's body axis and points perpendicular - to the rocket's axis of cylindrical symmetry. + Aerodynamic force acting along the x-axis of the rocket's body frame + as a function of time. Expressed in Newtons (N). Flight.R2 : Function - Resultant force perpendicular to rockets axis due to - aerodynamic forces as a function of time. Units in N. - Expressed as a function of time. Can be called or accessed - as array. - Direction 2 is in the rocket's body axis and points perpendicular - to the rocket's axis of cylindrical symmetry and direction 1. + Aerodynamic force acting along the y-axis of the rocket's body frame + as a function of time. Expressed in Newtons (N). Flight.R3 : Function - Resultant force in rockets axis due to aerodynamic forces - as a function of time. Units in N. Usually just drag. - Expressed as a function of time. Can be called or accessed - as array. - Direction 3 is in the rocket's body axis and points in the - direction of cylindrical symmetry. + Aerodynamic force acting along the z-axis of the rocket's body frame + as a function of time. Expressed in Newtons (N). Flight.M1 : Function - Resultant moment (torque) perpendicular to rockets axis due to - aerodynamic forces and eccentricity as a function of time. - Units in N*m. - Expressed as a function of time. Can be called or accessed - as array. - Direction 1 is in the rocket's body axis and points perpendicular - to the rocket's axis of cylindrical symmetry. + Aerodynamic moment acting along the x-axis of the rocket's body + frame as a function of time. Expressed in Newtons (N). Flight.M2 : Function - Resultant moment (torque) perpendicular to rockets axis due to - aerodynamic forces and eccentricity as a function of time. - Units in N*m. - Expressed as a function of time. Can be called or accessed - as array. - Direction 2 is in the rocket's body axis and points perpendicular - to the rocket's axis of cylindrical symmetry and direction 1. + Aerodynamic moment acting along the y-axis of the rocket's body + frame as a function of time. Expressed in Newtons (N). Flight.M3 : Function - Resultant moment (torque) in rockets axis due to aerodynamic - forces and eccentricity as a function of time. Units in N*m. - Expressed as a function of time. Can be called or accessed - as array. - Direction 3 is in the rocket's body axis and points in the - direction of cylindrical symmetry. + Aerodynamic moment acting along the z-axis of the rocket's body + frame as a function of time. Expressed in Newtons (N). Flight.net_thrust : Function Rocket's engine net thrust as a function of time in Newton. This is the actual thrust force experienced by the rocket. It may be corrected with the atmospheric pressure if a reference - pressure is defined. Can be called or accessed as array. + pressure is defined. Flight.aerodynamic_lift : Function Resultant force perpendicular to rockets axis due to aerodynamic effects as a function of time. Units in N. @@ -385,25 +355,25 @@ class Flight: as array. Flight.rail_button1_normal_force : Function Upper rail button normal force in N as a function - of time. Can be called or accessed as array. + of time. Flight.max_rail_button1_normal_force : float Maximum upper rail button normal force experienced during rail flight phase in N. Flight.rail_button1_shear_force : Function Upper rail button shear force in N as a function - of time. Can be called or accessed as array. + of time. Flight.max_rail_button1_shear_force : float Maximum upper rail button shear force experienced during rail flight phase in N. Flight.rail_button2_normal_force : Function Lower rail button normal force in N as a function - of time. Can be called or accessed as array. + of time. Flight.max_rail_button2_normal_force : float Maximum lower rail button normal force experienced during rail flight phase in N. Flight.rail_button2_shear_force : Function Lower rail button shear force in N as a function - of time. Can be called or accessed as array. + of time. Flight.max_rail_button2_shear_force : float Maximum lower rail button shear force experienced during rail flight phase in N. @@ -424,26 +394,26 @@ class Flight: Units in J. Flight.thrust_power : Function Rocket's engine thrust power output as a function - of time in Watts. Can be called or accessed as array. + of time in Watts. Flight.drag_power : Function Aerodynamic drag power output as a function - of time in Watts. Can be called or accessed as array. + of time in Watts. Flight.attitude_frequency_response : Function Fourier Frequency Analysis of the rocket's attitude angle. - Expressed as the absolute vale of the magnitude as a function - of frequency in Hz. Can be called or accessed as array. + Expressed as the absolute value of the magnitude as a function + of frequency in Hz. Flight.omega1_frequency_response : Function Fourier Frequency Analysis of the rocket's angular velocity omega 1. - Expressed as the absolute vale of the magnitude as a function - of frequency in Hz. Can be called or accessed as array. + Expressed as the absolute value of the magnitude as a function + of frequency in Hz. Flight.omega2_frequency_response : Function Fourier Frequency Analysis of the rocket's angular velocity omega 2. - Expressed as the absolute vale of the magnitude as a function - of frequency in Hz. Can be called or accessed as array. + Expressed as the absolute value of the magnitude as a function + of frequency in Hz. Flight.omega3_frequency_response : Function Fourier Frequency Analysis of the rocket's angular velocity omega 3. - Expressed as the absolute vale of the magnitude as a function - of frequency in Hz. Can be called or accessed as array. + Expressed as the absolute value of the magnitude as a function + of frequency in Hz. Flight.static_margin : Function Rocket's static margin during flight in calibers. Flight.stability_margin : Function @@ -454,22 +424,21 @@ class Flight: Rocket's stability margin in calibers when it leaves the rail. Flight.stream_velocity_x : Function Freestream velocity x (East) component, in m/s, as a function of - time. Can be called or accessed as array. + time. Flight.stream_velocity_y : Function Freestream velocity y (North) component, in m/s, as a function of - time. Can be called or accessed as array. + time. Flight.stream_velocity_z : Function Freestream velocity z (up) component, in m/s, as a function of - time. Can be called or accessed as array. + time. Flight.free_stream_speed : Function Freestream velocity magnitude, in m/s, as a function of time. - Can be called or accessed as array. Flight.apogee_freestream_speed : float Freestream speed of the rocket at apogee in m/s. Flight.mach_number : Function Rocket's Mach number defined as its freestream speed divided by the speed of sound at its altitude. Expressed - as a function of time. Can be called or accessed as array. + as a function of time. Flight.max_mach_number : float Rocket's maximum Mach number experienced during flight. Flight.max_mach_number_time : float @@ -477,7 +446,7 @@ class Flight: Flight.reynolds_number : Function Rocket's Reynolds number, using its diameter as reference length and free_stream_speed as reference velocity. Expressed - as a function of time. Can be called or accessed as array. + as a function of time. Flight.max_reynolds_number : float Rocket's maximum Reynolds number experienced during flight. Flight.max_reynolds_number_time : float @@ -485,14 +454,14 @@ class Flight: Flight.dynamic_pressure : Function Dynamic pressure experienced by the rocket in Pa as a function of time, defined by 0.5*rho*V^2, where rho is air density and V - is the freestream speed. Can be called or accessed as array. + is the freestream speed. Flight.max_dynamic_pressure : float Maximum dynamic pressure, in Pa, experienced by the rocket. Flight.max_dynamic_pressure_time : float Time at which the rocket experiences maximum dynamic pressure. Flight.total_pressure : Function Total pressure experienced by the rocket in Pa as a function - of time. Can be called or accessed as array. + of time. Flight.max_total_pressure : float Maximum total pressure, in Pa, experienced by the rocket. Flight.max_total_pressure_time : float @@ -2119,20 +2088,20 @@ def altitude(self): @funcify_method("Time (s)", "Vx (m/s)", "spline", "zero") def vx(self): - """Velocity of the rocket center of dry mass in the direction of - the rocket x-axis as a Function of time.""" + """Velocity of the rocket's center of dry mass in the X (East) direction + of the inertial frame as a function of time.""" return self.solution_array[:, [0, 4]] @funcify_method("Time (s)", "Vy (m/s)", "spline", "zero") def vy(self): - """Velocity of the rocket center of dry mass in the direction of - the rocket y-axis as a Function of time.""" + """Velocity of the rocket's center of dry mass in the Y (North) + direction of the inertial frame as a function of time.""" return self.solution_array[:, [0, 5]] @funcify_method("Time (s)", "Vz (m/s)", "spline", "zero") def vz(self): - """Velocity of the rocket center of dry mass in the direction of - the rocket z-axis as a Function of time.""" + """Velocity of the rocket's center of dry mass in the Z (Up) direction of + the inertial frame as a function of time.""" return self.solution_array[:, [0, 6]] @funcify_method("Time (s)", "e0", "spline", "constant") @@ -2157,94 +2126,100 @@ def e3(self): @funcify_method("Time (s)", "ω1 (rad/s)", "spline", "zero") def w1(self): - """Angular velocity of the rocket in the x-axis as a Function of time. - Sometimes referred to as pitch rate (q).""" + """Angular velocity of the rocket in the x direction of the rocket's + body frame as a function of time, in rad/s. Sometimes referred to as + pitch rate (q).""" return self.solution_array[:, [0, 11]] @funcify_method("Time (s)", "ω2 (rad/s)", "spline", "zero") def w2(self): - """Angular velocity of the rocket in the y-axis as a Function of time. - Sometimes referred to as yaw rate (r).""" + """Angular velocity of the rocket in the y direction of the rocket's + body frame as a function of time, in rad/s. Sometimes referred to as + yaw rate (r).""" return self.solution_array[:, [0, 12]] @funcify_method("Time (s)", "ω3 (rad/s)", "spline", "zero") def w3(self): - """Angular velocity of the rocket in the z-axis as a Function of time. - Sometimes referred to as roll rate (p).""" + """Angular velocity of the rocket in the z direction of the rocket's + body frame as a function of time, in rad/s. Sometimes referred to as + roll rate (p).""" return self.solution_array[:, [0, 13]] # Process second type of outputs - accelerations components @funcify_method("Time (s)", "Ax (m/s²)", "spline", "zero") def ax(self): - """Acceleration of the rocket center of dry mass in the direction of - the rocket x-axis as a Function of time.""" + """Acceleration of the rocket's center of dry mass along the X (East) + axis in the inertial frame as a function of time.""" return self.__evaluate_post_process[:, [0, 1]] @funcify_method("Time (s)", "Ay (m/s²)", "spline", "zero") def ay(self): - """Acceleration of the rocket center of dry mass in the direction of - the rocket y-axis as a Function of time.""" + """Acceleration of the rocket's center of dry mass along the Y (North) + axis in the inertial frame as a function of time.""" return self.__evaluate_post_process[:, [0, 2]] @funcify_method("Time (s)", "Az (m/s²)", "spline", "zero") def az(self): - """Acceleration of the rocket center of dry mass in the direction of - the rocket z-axis as a Function of time.""" + """Acceleration of the rocket's center of dry mass along the Z (Up) + axis in the inertial frame as a function of time.""" return self.__evaluate_post_process[:, [0, 3]] @funcify_method("Time (s)", "α1 (rad/s²)", "spline", "zero") def alpha1(self): - """Angular acceleration of the rocket in the x-axis as a Function of - time. Sometimes referred to as pitch acceleration.""" + """Angular acceleration of the rocket in the x direction of the rocket's + body frame as a function of time, in rad/s. Sometimes referred to as + pitch acceleration.""" return self.__evaluate_post_process[:, [0, 4]] @funcify_method("Time (s)", "α2 (rad/s²)", "spline", "zero") def alpha2(self): - """Angular acceleration of the rocket in the y-axis as a Function of - time. Sometimes referred to as yaw acceleration.""" + """Angular acceleration of the rocket in the y direction of the rocket's + body frame as a function of time, in rad/s. Sometimes referred to as + yaw acceleration.""" return self.__evaluate_post_process[:, [0, 5]] @funcify_method("Time (s)", "α3 (rad/s²)", "spline", "zero") def alpha3(self): - """Angular acceleration of the rocket in the z-axis as a Function of - time. Sometimes referred to as roll acceleration.""" + """Angular acceleration of the rocket in the z direction of the rocket's + body frame as a function of time, in rad/s. Sometimes referred to as + roll acceleration.""" return self.__evaluate_post_process[:, [0, 6]] # Process third type of outputs - Temporary values @funcify_method("Time (s)", "R1 (N)", "spline", "zero") def R1(self): - """Aerodynamic force along the x-axis of the rocket as a Function of - time.""" + """Aerodynamic force acting along the x-axis of the rocket's body frame + as a function of time. Expressed in Newtons (N).""" return self.__evaluate_post_process[:, [0, 7]] @funcify_method("Time (s)", "R2 (N)", "spline", "zero") def R2(self): - """Aerodynamic force along the y-axis of the rocket as a Function of - time.""" + """Aerodynamic force acting along the y-axis of the rocket's body frame + as a function of time. Expressed in Newtons (N).""" return self.__evaluate_post_process[:, [0, 8]] @funcify_method("Time (s)", "R3 (N)", "spline", "zero") def R3(self): - """Aerodynamic force along the z-axis (rocket's axis of symmetry) as a - Function of time.""" + """Aerodynamic force acting along the z-axis of the rocket's body frame + as a function of time. Expressed in Newtons (N).""" return self.__evaluate_post_process[:, [0, 9]] @funcify_method("Time (s)", "M1 (Nm)", "linear", "zero") def M1(self): - """Aerodynamic moment in the rocket x-axis as a Function of time. - Sometimes referred to as pitch moment.""" + """Aerodynamic moment acting along the x-axis of the rocket's body + frame as a function of time. Expressed in Newtons (N).""" return self.__evaluate_post_process[:, [0, 10]] @funcify_method("Time (s)", "M2 (Nm)", "linear", "zero") def M2(self): - """Aerodynamic moment in the rocket y-axis as a Function of time. - Sometimes referred to as yaw moment.""" + """Aerodynamic moment acting along the y-axis of the rocket's body + frame as a function of time. Expressed in Newtons (N).""" return self.__evaluate_post_process[:, [0, 11]] @funcify_method("Time (s)", "M3 (Nm)", "linear", "zero") def M3(self): - """Aerodynamic moment in the rocket z-axis as a Function of time. - Sometimes referred to as roll moment.""" + """Aerodynamic moment acting along the z-axis of the rocket's body + frame as a function of time. Expressed in Newtons (N).""" return self.__evaluate_post_process[:, [0, 12]] @funcify_method("Time (s)", "Net Thrust (N)", "linear", "zero") @@ -2287,6 +2262,86 @@ def wind_velocity_y(self): # Process fourth type of output - values calculated from previous outputs + # Frame conversions + + @cached_property + def _stacked_velocity_body_frame(self): + """Stacked velocity array at the center of dry mass in the body frame + at each time step.""" + Kt = self.direction_cosine_matrixes + v = np.array( + [ + self.vx.y_array, + self.vy.y_array, + self.vz.y_array, + ] + ).transpose() + stacked_velocity_body_frame = np.squeeze(np.matmul(Kt, v[:, :, np.newaxis])) + return stacked_velocity_body_frame + + @funcify_method("Time (s)", "Vx Body Frame (m/s)", "spline", "zero") + def vx_body_frame(self): + """Velocity of the rocket's center of dry mass along the x axis of the + body frame as a function of time.""" + return np.array( + [self.time, self._stacked_velocity_body_frame[:, 0]] + ).transpose() + + @funcify_method("Time (s)", "Vy Body Frame (m/s)", "spline", "zero") + def vy_body_frame(self): + """Velocity of the rocket's center of dry mass along the y axis of the + body frame as a function of time.""" + return np.array( + [self.time, self._stacked_velocity_body_frame[:, 1]] + ).transpose() + + @funcify_method("Time (s)", "Vz Body Frame (m/s)", "spline", "zero") + def vz_body_frame(self): + """Velocity of the rocket's center of dry mass along the z axis of the + body frame as a function of time.""" + return np.array( + [self.time, self._stacked_velocity_body_frame[:, 2]] + ).transpose() + + @cached_property + def _stacked_acceleration_body_frame(self): + """Stacked acceleration array at the center of dry mass in the body + frame at each time step.""" + Kt = self.direction_cosine_matrixes + a = np.array( + [ + self.ax.y_array, + self.ay.y_array, + self.az.y_array, + ] + ).transpose() + stacked_acceleration_body_frame = np.squeeze(np.matmul(Kt, a[:, :, np.newaxis])) + return stacked_acceleration_body_frame + + @funcify_method("Time (s)", "Ax Body Frame (m/s²)", "spline", "zero") + def ax_body_frame(self): + """Acceleration of the rocket's center of dry mass along the x axis of the + body frame as a function of time.""" + return np.array( + [self.time, self._stacked_acceleration_body_frame[:, 0]] + ).transpose() + + @funcify_method("Time (s)", "Ay Body Frame (m/s²)", "spline", "zero") + def ay_body_frame(self): + """Acceleration of the rocket's center of dry mass along the y axis of the + body frame as a function of time.""" + return np.array( + [self.time, self._stacked_acceleration_body_frame[:, 1]] + ).transpose() + + @funcify_method("Time (s)", "Az Body Frame (m/s²)", "spline", "zero") + def az_body_frame(self): + """Acceleration of the rocket's center of dry mass along the z axis of the + body frame as a function of time.""" + return np.array( + [self.time, self._stacked_acceleration_body_frame[:, 2]] + ).transpose() + # Kinematics functions and values # Velocity Magnitude @funcify_method("Time (s)", "Speed - Velocity Magnitude (m/s)") diff --git a/tests/integration/test_sensor.py b/tests/integration/test_sensor.py index 2d98326b3..07b57b61b 100644 --- a/tests/integration/test_sensor.py +++ b/tests/integration/test_sensor.py @@ -45,11 +45,14 @@ def test_accelerometer(self): """Test an ideal accelerometer.""" accelerometer = self.flight.rocket.sensors[0].component time, ax, ay, az = zip(*accelerometer.measured_data[0]) - a = np.sqrt(np.array(ax) ** 2 + np.array(ay) ** 2 + np.array(az) ** 2) - sim_accel = self.flight.acceleration(time) + sim_ax = self.flight.ax_body_frame(time) + sim_ay = self.flight.ay_body_frame(time) + sim_az = self.flight.az_body_frame(time) + + assert np.allclose(ax, sim_ax, atol=1e-12) + assert np.allclose(ay, sim_ay, atol=1e-12) + assert np.allclose(az, sim_az, atol=1e-12) - # tolerance is bounded to numerical errors in the transformation matrixes - assert np.allclose(a, sim_accel, atol=1e-12) # check if both added accelerometer instances saved the same data assert ( self.flight.sensors[0].measured_data[0] @@ -60,12 +63,13 @@ def test_gyroscope(self): """Test an ideal gyroscope.""" gyroscope = self.flight.rocket.sensors[2].component time, wx, wy, wz = zip(*gyroscope.measured_data) - w = np.sqrt(np.array(wx) ** 2 + np.array(wy) ** 2 + np.array(wz) ** 2) - flight_wx = np.array(self.flight.w1(time)) - flight_wy = np.array(self.flight.w2(time)) - flight_wz = np.array(self.flight.w3(time)) - sim_w = np.sqrt(flight_wx**2 + flight_wy**2 + flight_wz**2) - assert np.allclose(w, sim_w, atol=1e-12) + sim_wx = self.flight.w1(time) + sim_wy = self.flight.w2(time) + sim_wz = self.flight.w3(time) + + assert np.allclose(wx, sim_wx, atol=1e-12) + assert np.allclose(wy, sim_wy, atol=1e-12) + assert np.allclose(wz, sim_wz, atol=1e-12) def test_barometer(self): """Test an ideal barometer.""" diff --git a/tests/unit/test_sensor.py b/tests/unit/test_sensor.py index eb3d194b4..0813a3638 100644 --- a/tests/unit/test_sensor.py +++ b/tests/unit/test_sensor.py @@ -76,7 +76,9 @@ def test_rotation_matrix(noisy_rotated_accelerometer): [0.7499999999999999, 0.43301270189221946, 0.5000000000000001], ] ) - rotation_matrix = np.array(noisy_rotated_accelerometer.rotation_matrix.components) + rotation_matrix = np.array( + noisy_rotated_accelerometer.rotation_sensor_to_body.components + ) assert np.allclose(expected_matrix, rotation_matrix, atol=1e-8) @@ -291,7 +293,7 @@ def test_noisy_rotated_accelerometer(noisy_rotated_accelerometer, example_plain_ euler313_to_quaternions(*np.deg2rad([60, 60, 60])) ) total_rotation = sensor_rotation @ cross_axis_sensitivity - rocket_rotation = Matrix.transformation(U[6:10]) + rocket_rotation = Matrix.transformation(U[6:10]).transpose # expected measurement without noise ax, ay, az = total_rotation @ (rocket_rotation @ acceleration) # expected measurement with constant bias @@ -334,9 +336,8 @@ def test_noisy_rotated_gyroscope(noisy_rotated_gyroscope, example_plain_env): euler313_to_quaternions(*np.deg2rad([-60, -60, -60])) ) total_rotation = sensor_rotation @ cross_axis_sensitivity - rocket_rotation = Matrix.transformation(U[6:10]) # expected measurement without noise - wx, wy, wz = total_rotation @ (rocket_rotation @ omega) + wx, wy, wz = total_rotation @ omega # expected measurement with constant bias wx += 0.5 wy += 0.5