@@ -1197,6 +1197,10 @@ def __init_equations_of_motion(self):
11971197 if self .equations_of_motion == "solid_propulsion" :
11981198 # NOTE: The u_dot is faster, but only works for solid propulsion
11991199 self .u_dot_generalized = self .u_dot
1200+ if self .equations_of_motion == "2" :
1201+ self .u_dot_generalized = self .u_dot_generalized_std
1202+ if self .equations_of_motion == "3" :
1203+ self .u_dot_generalized = self .u_dot_generalized_moving_omega
12001204
12011205 def __init_controllers (self ):
12021206 """Initialize controllers and sensors"""
@@ -1935,6 +1939,216 @@ def u_dot_generalized(self, t, u, post_processing=False): # pylint: disable=too
19351939
19361940 return u_dot
19371941
1942+ def u_dot_generalized_std (self , t , u , post_processing = False ): # pylint: disable=too-many-locals,too-many-statements
1943+ """Calculates derivative of u state vector with respect to time when the
1944+ rocket is flying in 6 DOF motion in space and significant mass variation
1945+ effects exist. Typical flight phases include powered ascent after launch
1946+ rail.
1947+
1948+ Parameters
1949+ ----------
1950+ t : float
1951+ Time in seconds
1952+ u : list
1953+ State vector defined by u = [x, y, z, vx, vy, vz, q0, q1,
1954+ q2, q3, omega1, omega2, omega3].
1955+ post_processing : bool, optional
1956+ If True, adds flight data information directly to self variables
1957+ such as self.angle_of_attack, by default False.
1958+
1959+ Returns
1960+ -------
1961+ u_dot : list
1962+ State vector defined by u_dot = [vx, vy, vz, ax, ay, az,
1963+ e0_dot, e1_dot, e2_dot, e3_dot, alpha1, alpha2, alpha3].
1964+ """
1965+ # Retrieve integration data
1966+ _ , _ , z , vx , vy , vz , e0 , e1 , e2 , e3 , omega1 , omega2 , omega3 = u
1967+
1968+ # Create necessary vectors
1969+ # r = Vector([x, y, z]) # CDM position vector
1970+ v = Vector ([vx , vy , vz ]) # CDM velocity vector
1971+ e = [e0 , e1 , e2 , e3 ] # Euler parameters/quaternions
1972+ w = Vector ([omega1 , omega2 , omega3 ]) # Angular velocity vector
1973+
1974+ # Retrieve necessary quantities
1975+ ## Rocket mass
1976+ total_mass = self .rocket .total_mass .get_value_opt (t )
1977+ total_mass_dot = self .rocket .total_mass_flow_rate .get_value_opt (t )
1978+ total_mass_ddot = self .rocket .total_mass_flow_rate .differentiate_complex_step (t )
1979+ ## CM position vector and time derivatives relative to CDM in body frame
1980+ r_CM_z = self .rocket .com_to_cdm_function
1981+ r_CM_t = r_CM_z .get_value_opt (t )
1982+ r_CM = Vector ([0 , 0 , r_CM_t ])
1983+ r_CM_dot = Vector ([0 , 0 , r_CM_z .differentiate_complex_step (t )])
1984+ r_CM_ddot = Vector ([0 , 0 , r_CM_z .differentiate (t , order = 2 )])
1985+ ## Nozzle position vector
1986+ r_NOZ = Vector ([0 , 0 , self .rocket .nozzle_to_cdm ])
1987+ ## Nozzle gyration tensor
1988+ S_nozzle = self .rocket .nozzle_gyration_tensor
1989+ ## Inertia tensor
1990+ inertia_tensor = self .rocket .get_inertia_tensor_at_time (t )
1991+ ## Inertia tensor time derivative in the body frame
1992+ I_dot = self .rocket .get_inertia_tensor_derivative_at_time (t )
1993+
1994+ # Calculate the Inertia tensor relative to CM
1995+ H = (r_CM .cross_matrix @ - r_CM .cross_matrix ) * total_mass
1996+ I_CM = inertia_tensor - H
1997+
1998+ # Prepare transformation matrices
1999+ K = Matrix .transformation (e )
2000+ Kt = K .transpose
2001+
2002+ # Compute aerodynamic forces and moments
2003+ R1 , R2 , R3 , M1 , M2 , M3 = 0 , 0 , 0 , 0 , 0 , 0
2004+
2005+ ## Drag force
2006+ rho = self .env .density .get_value_opt (z )
2007+ wind_velocity_x = self .env .wind_velocity_x .get_value_opt (z )
2008+ wind_velocity_y = self .env .wind_velocity_y .get_value_opt (z )
2009+ wind_velocity = Vector ([wind_velocity_x , wind_velocity_y , 0 ])
2010+ free_stream_speed = abs ((wind_velocity - Vector (v )))
2011+ speed_of_sound = self .env .speed_of_sound .get_value_opt (z )
2012+ free_stream_mach = free_stream_speed / speed_of_sound
2013+
2014+ if self .rocket .motor .burn_start_time < t < self .rocket .motor .burn_out_time :
2015+ pressure = self .env .pressure .get_value_opt (z )
2016+ net_thrust = max (
2017+ self .rocket .motor .thrust .get_value_opt (t )
2018+ + self .rocket .motor .pressure_thrust (pressure ),
2019+ 0 ,
2020+ )
2021+ drag_coeff = self .rocket .power_on_drag .get_value_opt (free_stream_mach )
2022+ else :
2023+ net_thrust = 0
2024+ drag_coeff = self .rocket .power_off_drag .get_value_opt (free_stream_mach )
2025+ R3 += - 0.5 * rho * (free_stream_speed ** 2 ) * self .rocket .area * drag_coeff
2026+ for air_brakes in self .rocket .air_brakes :
2027+ if air_brakes .deployment_level > 0 :
2028+ air_brakes_cd = air_brakes .drag_coefficient .get_value_opt (
2029+ air_brakes .deployment_level , free_stream_mach
2030+ )
2031+ air_brakes_force = (
2032+ - 0.5
2033+ * rho
2034+ * (free_stream_speed ** 2 )
2035+ * air_brakes .reference_area
2036+ * air_brakes_cd
2037+ )
2038+ if air_brakes .override_rocket_drag :
2039+ R3 = air_brakes_force # Substitutes rocket drag coefficient
2040+ else :
2041+ R3 += air_brakes_force
2042+ # Get rocket velocity in body frame
2043+ velocity_in_body_frame = Kt @ v
2044+ # Calculate lift and moment for each component of the rocket
2045+ for aero_surface , _ in self .rocket .aerodynamic_surfaces :
2046+ # Component cp relative to CDM in body frame
2047+ comp_cp = self .rocket .surfaces_cp_to_cdm [aero_surface ]
2048+ # Component absolute velocity in body frame
2049+ comp_vb = velocity_in_body_frame + (w ^ comp_cp )
2050+ # Wind velocity at component altitude
2051+ comp_z = z + (K @ comp_cp ).z
2052+ comp_wind_vx = self .env .wind_velocity_x .get_value_opt (comp_z )
2053+ comp_wind_vy = self .env .wind_velocity_y .get_value_opt (comp_z )
2054+ # Component freestream velocity in body frame
2055+ comp_wind_vb = Kt @ Vector ([comp_wind_vx , comp_wind_vy , 0 ])
2056+ comp_stream_velocity = comp_wind_vb - comp_vb
2057+ comp_stream_speed = abs (comp_stream_velocity )
2058+ comp_stream_mach = comp_stream_speed / speed_of_sound
2059+ # Reynolds at component altitude
2060+ # TODO: Reynolds is only used in generic surfaces. This calculation
2061+ # should be moved to the surface class for efficiency
2062+ comp_reynolds = (
2063+ self .env .density .get_value_opt (comp_z )
2064+ * comp_stream_speed
2065+ * aero_surface .reference_length
2066+ / self .env .dynamic_viscosity .get_value_opt (comp_z )
2067+ )
2068+ # Forces and moments
2069+ X , Y , Z , M , N , L = aero_surface .compute_forces_and_moments (
2070+ comp_stream_velocity ,
2071+ comp_stream_speed ,
2072+ comp_stream_mach ,
2073+ rho ,
2074+ comp_cp ,
2075+ w ,
2076+ comp_reynolds ,
2077+ )
2078+ R1 += X
2079+ R2 += Y
2080+ R3 += Z
2081+ M1 += M
2082+ M2 += N
2083+ M3 += L
2084+
2085+ # Off center moment
2086+ M1 += (
2087+ self .rocket .cp_eccentricity_y * R3
2088+ + self .rocket .thrust_eccentricity_y * net_thrust
2089+ )
2090+ M2 -= (
2091+ self .rocket .cp_eccentricity_x * R3
2092+ + self .rocket .thrust_eccentricity_x * net_thrust
2093+ )
2094+ M3 += self .rocket .cp_eccentricity_x * R2 - self .rocket .cp_eccentricity_y * R1
2095+
2096+ weight_in_body_frame = Kt @ Vector (
2097+ [0 , 0 , - total_mass * self .env .gravity .get_value_opt (z )]
2098+ )
2099+
2100+ T00 = total_mass * r_CM
2101+ T03 = 2 * total_mass_dot * (r_NOZ - r_CM ) - 2 * total_mass * r_CM_dot
2102+ T04 = (
2103+ Vector ([0 , 0 , net_thrust ])
2104+ - total_mass * r_CM_ddot
2105+ - 2 * total_mass_dot * r_CM_dot
2106+ + total_mass_ddot * (r_NOZ - r_CM )
2107+ )
2108+ T05 = total_mass_dot * S_nozzle - I_dot
2109+
2110+ T20 = (
2111+ ((w ^ T00 ) ^ w )
2112+ + (w ^ T03 )
2113+ + T04
2114+ + weight_in_body_frame
2115+ + Vector ([R1 , R2 , R3 ])
2116+ )
2117+
2118+ T21 = (
2119+ ((inertia_tensor @ w ) ^ w )
2120+ + T05 @ w
2121+ - (weight_in_body_frame ^ r_CM )
2122+ + Vector ([M1 , M2 , M3 ])
2123+ )
2124+
2125+ # Angular velocity derivative
2126+ w_dot = I_CM .inverse @ (T21 + (T20 ^ r_CM ))
2127+
2128+ # Velocity vector derivative + Coriolis acceleration
2129+ v_dot = K @ (T20 / total_mass - (r_CM ^ w_dot ))
2130+
2131+ # Euler parameters derivative
2132+ e_dot = [
2133+ 0.5 * (- omega1 * e1 - omega2 * e2 - omega3 * e3 ),
2134+ 0.5 * (omega1 * e0 + omega3 * e2 - omega2 * e3 ),
2135+ 0.5 * (omega2 * e0 - omega3 * e1 + omega1 * e3 ),
2136+ 0.5 * (omega3 * e0 + omega2 * e1 - omega1 * e2 ),
2137+ ]
2138+
2139+ # Position vector derivative
2140+ r_dot = [vx , vy , vz ]
2141+
2142+ # Create u_dot
2143+ u_dot = [* r_dot , * v_dot , * e_dot , * w_dot ]
2144+
2145+ if post_processing :
2146+ self .__post_processed_variables .append (
2147+ [t , * v_dot , * w_dot , R1 , R2 , R3 , M1 , M2 , M3 , net_thrust ]
2148+ )
2149+
2150+ return u_dot
2151+
19382152 def u_dot_parachute (self , t , u , post_processing = False ):
19392153 """Calculates derivative of u state vector with respect to time
19402154 when rocket is flying under parachute. A 3 DOF approximation is
@@ -2001,6 +2215,12 @@ def u_dot_parachute(self, t, u, post_processing=False):
20012215 ay = Dy / (mp + ma )
20022216 az = (Dz - 9.8 * mp ) / (mp + ma )
20032217
2218+ # Add coriolis acceleration
2219+ _ , w_earth_y , w_earth_z = self .env .earth_rotation_vector
2220+ ax -= 2 * (vz * w_earth_y - vy * w_earth_z )
2221+ ay -= 2 * (vx * w_earth_z )
2222+ az -= 2 * (- vx * w_earth_y )
2223+
20042224 if post_processing :
20052225 self .__post_processed_variables .append (
20062226 [t , ax , ay , az , 0 , 0 , 0 , Dx , Dy , Dz , 0 , 0 , 0 , 0 ]
0 commit comments