@ -18,17 +18,17 @@ void orbital_elements_to_cartesian(OrbitalElements elements, double parent_mass,
if ( fabs ( e - 1.0 ) < PARABOLIC_TOLERANCE ) {
p = elements . semi_latus_rectum ;
} else {
p = a * ( 1.0 - e * e ) ;
p = a * ( 1.0 - e * e ) ; // Semi-latus rectum: p = a(1-e²)
}
double r = p / ( 1.0 + e * cos_nu ) ;
double r = p / ( 1.0 + e * cos_nu ) ; // Polar equation of orbit
double x_orbital = r * cos_nu ;
double y_orbital = r * sin_nu ;
Vec3 position = { x_orbital , y_orbital , 0.0 } ;
double vx_orbital = - sqrt ( mu / p ) * sin_nu ;
double vx_orbital = - sqrt ( mu / p ) * sin_nu ; // Velocity from vis-viva equation
double vy_orbital = sqrt ( mu / p ) * ( e + cos_nu ) ;
Vec3 velocity = { vx_orbital , vy_orbital , 0.0 } ;
@ -37,7 +37,7 @@ void orbital_elements_to_cartesian(OrbitalElements elements, double parent_mass,
double i = elements . inclination ;
double Omega = elements . longitude_of_ascending_node ;
Mat3 rotation = mat3_rotation_orbital ( omega , i , Omega ) ;
Mat3 rotation = mat3_rotation_orbital ( omega , i , Omega ) ; // z-x-z Euler angles: R_z(Ω) · R_x(i) · R_z(ω)
* out_position = mat3_multiply_vec3 ( rotation , position ) ;
* out_velocity = mat3_multiply_vec3 ( rotation , velocity ) ;
@ -90,6 +90,7 @@ double solve_kepler_hyperbolic(double mean_anomaly, double eccentricity) {
double eccentric_to_true_anomaly ( double eccentric_anomaly , double eccentricity ) {
if ( fabs ( 1.0 - eccentricity ) < 0.01 ) {
// Near-parabolic: use cos/sin formulation to avoid numeric instability
double E = eccentric_anomaly ;
double e = eccentricity ;
@ -106,7 +107,7 @@ double eccentric_to_true_anomaly(double eccentric_anomaly, double eccentricity)
return atan2 ( sin_nu , cos_nu ) ;
}
double tan_half_E = tan ( eccentric_anomaly / 2.0 ) ;
double tan_half_E = tan ( eccentric_anomaly / 2.0 ) ; // Tangent half-angle formula: tan(ν/2) = √((1+e)/(1-e)) · tan(E/2)
double tan_half_nu = sqrt ( ( 1.0 + eccentricity ) / ( 1.0 - eccentricity ) ) * tan_half_E ;
return 2.0 * atan ( tan_half_nu ) ;
}
@ -118,6 +119,7 @@ double hyperbolic_to_true_anomaly(double hyperbolic_anomaly, double eccentricity
return 2.0 * atanh ( tanh_half_nu ) ;
}
// Conversion chain: M → E/H → ν
double mean_anomaly_to_true_anomaly ( double mean_anomaly , double eccentricity ) {
if ( eccentricity < 1.0 ) {
double E = solve_kepler_elliptical ( mean_anomaly , eccentricity ) ;
@ -140,8 +142,9 @@ OrbitalElements cartesian_to_orbital_elements(Vec3 position, Vec3 velocity, doub
double v = vec3_magnitude ( v_vec ) ;
double v_squared = v * v ;
double specific_energy = - mu / r + v_squared / 2.0 ;
double specific_energy = - mu / r + v_squared / 2.0 ; // Specific orbital energy: ε = v²/2 - μ/r
double h = vec3_magnitude ( h_vec ) ;
// Eccentricity vector: e_vec = (v² - μ/r)r_vec - (r_vec·v_vec)v_vec
double e_vec_x = ( ( v_squared - mu / r ) * r_vec . x - ( vec3_dot ( r_vec , v_vec ) ) * v_vec . x ) / mu ;
double e_vec_y = ( ( v_squared - mu / r ) * r_vec . y - ( vec3_dot ( r_vec , v_vec ) ) * v_vec . y ) / mu ;
@ -151,15 +154,17 @@ OrbitalElements cartesian_to_orbital_elements(Vec3 position, Vec3 velocity, doub
double e = vec3_magnitude ( e_vec ) ;
double a ;
// Near-parabolic: energy too close to zero, use large value
if ( fabs ( specific_energy ) < 1e-10 ) {
a = 1e10 ;
} else {
a = - mu / ( 2.0 * specific_energy ) ;
a = - mu / ( 2.0 * specific_energy ) ; // Semi-major axis: a = -μ/(2ε)
}
double r_mag = vec3_magnitude ( r_vec ) ;
double r_dot_e = vec3_dot ( r_vec , e_vec ) ;
// Near-circular: e ≈ 0, true anomaly undefined
double true_anomaly ;
if ( e < 1e-10 ) {
true_anomaly = 0.0 ;
@ -191,15 +196,17 @@ OrbitalElements cartesian_to_orbital_elements(Vec3 position, Vec3 velocity, doub
double i ;
double h_z = h_vec . z ;
if ( h > 1e-10 ) {
i = acos ( h_z / h ) ;
i = acos ( h_z / h ) ; // Inclination: i = acos(h_z / h)
} else {
i = 0.0 ;
}
// Ascending node vector: n = k × h_vec (k is unit Z vector)
Vec3 n_vec = { 0.0 , 0.0 , 1.0 } ;
Vec3 n = vec3_cross ( n_vec , h_vec ) ;
double n_mag = vec3_magnitude ( n ) ;
// Longitude of ascending node from n vector
double Omega ;
if ( n_mag > 1e-10 ) {
Omega = acos ( n . x / n_mag ) ;
@ -209,6 +216,7 @@ OrbitalElements cartesian_to_orbital_elements(Vec3 position, Vec3 velocity, doub
} else {
Omega = 0.0 ;
}
// Argument of periapsis: ω = atan2(n×e·h, e·n)
double omega ;
if ( e > 1e-10 & & n_mag > 1e-10 ) {