Archives
Trending
Support
Login
clear text
XML
Django
JavaScript
MATLAB
C
C++
C#
Python
SQL
Shell
Bash
Markdown
YAML
JSON
HTML
CSS
PHP
Java
Ruby
Go
Rust
Swift
Kotlin
Arduino
TypeScript
Perl
Autohotkey
Lua
SQF
R
Scala
Haskell
Groovy
Dart
Clojure
VB.NET
Objective-C
PowerShell
Bash
CoffeeScript
Verilog
// flight_model.rs use crate::aircraft::AircraftConfig; use crate::consts::PHYSICS_DT; #[derive(Clone)] pub struct FlightModel { // Airplane position in flight model coordinate system // Note: airplane_x = North direction, airplane_y = East direction // This differs from standard ENU where X=East, Y=North pub airplane_x: f64, // North position (feet) pub airplane_y: f64, // East position (feet) pub airplane_z: f64, // Altitude (feet) // Angles pub side_tilt: f64, // sideTiltRadians in banks.c pub forward_tilt: f64, // forwardTiltRadians in banks.c pub compass: f64, // compassRadians in banks.c // Flight parameters pub speed: f64, // target speed (throttle setting) pub speed_feet: f64, // current speed in feet/sec // User inputs pub left_right: f64, // for turning pub up_down: f64, // for pitch // Autopilot system pub autopilot_enabled: bool, pub target_altitude: f64, pub target_heading: f64, // new state for the PD controller pub autopilot_prev_hdg_error: f64, pub autopilot_prev_alt_error: f64, pub autopilot_filtered_hdg_deriv: f64, // Filtered derivative term to reduce noise // Waypoint navigation pub waypoint_enabled: bool, pub waypoints: Vec
, pub current_waypoint_index: usize, pub waypoint_target_heading: f64, // The actual bearing to waypoint (for HUD display) // HUD display options pub show_coordinates: bool, // Toggle coordinate display with X key pub show_scene_labels: bool, // Toggle scene origin labels with M key // Global coordinate system (set by event loop during recentering) pub current_lat: f64, pub current_lon: f64, // Reference coordinates for ENU conversion (set by event loop) pub ref_lat: f64, pub ref_lon: f64, engine_count: u32, power_hp: f64, mass_slugs: f64, cd0: f64, cl_max: f64, aspect_ratio: f64, critical_aoa_rad: f64, stall_pitch_down_rad: f64, stall_speed_fps: f64, thrust_lbf: f64, // Rotation matrix (camera transform) pub r11: f64, pub r12: f64, pub r13: f64, pub r21: f64, pub r22: f64, pub r23: f64, pub r31: f64, pub r32: f64, pub r33: f64, // Additional physics variables from banks.c d: f64, f: f64, p: f64, t: f64, v: f64, w: f64, m: f64, e: f64, i: f64, h: f64, x: f64, gravity_accel: f64, s: f64, } impl FlightModel { pub fn new(config: AircraftConfig) -> Self { let mut model = Self { airplane_x: 0.0, airplane_y: 0.0, airplane_z: config.initial_altitude_ft, side_tilt: 0.0, forward_tilt: config.initial_forward_tilt_rad, compass: 0.0, speed: 8.0, // Still fixed throttle steps for now speed_feet: config.initial_speed_fps, left_right: 0.0, up_down: 0.0, // Initialize autopilot system autopilot_enabled: false, target_altitude: 0.0, target_heading: 0.0, autopilot_prev_hdg_error: 0.0, autopilot_prev_alt_error: 0.0, autopilot_filtered_hdg_deriv: 0.0, // Initialize waypoint navigation waypoint_enabled: false, waypoints: Vec::new(), current_waypoint_index: 0, waypoint_target_heading: 0.0, // HUD display options show_coordinates: true, // Enabled by default, toggle with X key show_scene_labels: true, // Enabled by default, toggle with M key // Global coordinate system (set by event loop during recentering) current_lat: 37.6213, // Default SFO coordinates current_lon: -122.3790, // Reference coordinates for ENU conversion (set by event loop) ref_lat: 37.6213, // Default SFO coordinates ref_lon: -122.3790, r11: 1.0, r12: 0.0, r13: 0.0, r21: 0.0, r22: 1.0, r23: 0.0, r31: 0.0, r32: 0.0, r33: 1.0, d: 0.0, f: 0.0, p: 0.0, t: 0.0, v: 0.0, w: 0.0, m: 0.0, e: 0.0, i: 0.0, h: 0.0, x: config.initial_lift_param, gravity_accel: config.gravity_accel, s: config.wing_area_ft2, mass_slugs: config.mass_lb / 32.174, engine_count: config.engine_count, // Aircraft config power_hp: config.power_hp, cd0: config.cd0, cl_max: config.cl_max, aspect_ratio: config.aspect_ratio, critical_aoa_rad: config.critical_aoa_rad, stall_pitch_down_rad: config.stall_pitch_down_rad, stall_speed_fps: config.stall_speed_kts * 1.68781, thrust_lbf: config.thrust_lbf, }; model.calculate_angles(); model } // Throttle control - increases target speed (directly impacts acceleration) pub fn increase_throttle(&mut self) { self.speed += 1.0; } // Throttle control - decreases target speed pub fn decrease_throttle(&mut self) { self.speed -= 1.0; if self.speed < 0.0 { self.speed = 0.0; // Prevent negative speed } } /// Calculate the new orientation angles and rotation matrix pub fn calculate_angles(&mut self) { // Trigonometric precomputations let cos_fwd = self.forward_tilt.cos(); let sin_fwd = self.forward_tilt.sin(); let cos_cmp = self.compass.cos(); let cos_side = self.side_tilt.cos(); let sin_side = self.side_tilt.sin(); let sin_cmp = self.compass.sin(); // Update F (moment arm) self.f += PHYSICS_DT * self.p; // Yaw (compass heading) self.compass += cos_side * PHYSICS_DT * self.f / cos_fwd + self.d / cos_fwd * sin_side * PHYSICS_DT; // Pitch (forward/back tilt) self.forward_tilt += self.d * PHYSICS_DT * cos_side - PHYSICS_DT * self.f * sin_side; // Optional: crude stall behavior (nose drop) if self.speed_feet < self.stall_speed_fps && self.forward_tilt > self.critical_aoa_rad { self.forward_tilt = self.stall_pitch_down_rad; } // Roll (sideways tilt) self.side_tilt += (sin_side * self.d / cos_fwd * sin_fwd + self.v + sin_fwd / cos_fwd * self.f * cos_side) * PHYSICS_DT; // Build rotation matrix self.r11 = cos_fwd * cos_cmp; self.r12 = cos_fwd * sin_cmp; self.r13 = -sin_fwd; self.r21 = cos_cmp * sin_side * sin_fwd - sin_cmp * cos_side; self.r22 = cos_side * cos_cmp + sin_side * sin_cmp * sin_fwd; self.r23 = sin_side * cos_fwd; self.r31 = sin_cmp * sin_side + cos_side * sin_fwd * cos_cmp; self.r32 = sin_fwd * sin_cmp * cos_side - sin_side * cos_cmp; self.r33 = cos_side * cos_fwd; } /// Update physics: momentum, position, forces, and rates pub fn update_physics(&mut self) { // 1) Momentum let local_momentum = self.h * PHYSICS_DT; self.m += local_momentum; // 2) Inertia self.i = self.m / self.speed_feet; // 3) Position update (world → airplane reference) self.airplane_x += (self.r11 * self.speed_feet + self.r21 * self.m + self.r31 * self.x) * PHYSICS_DT; self.airplane_y += (self.r12 * self.speed_feet + self.i * self.m + self.r32 * self.x) * PHYSICS_DT; self.airplane_z += (-self.r13 * self.speed_feet - self.r23 * self.m - self.r33 * self.x) * PHYSICS_DT; // 4) Intermediate values let local_intermediate = 15.0 * self.f / self.speed_feet; self.e = 0.1 + self.x * 4.9 / self.speed_feet; self.t = self.x * self.x + self.speed_feet * self.speed_feet + self.m * self.m; // now using momentum squared let t_temp = self.t * local_intermediate / 32.0 - self.i * self.t / 24.0; // 5) H (for next momentum) self.h = self.gravity_accel * self.r23 + self.v * self.x - self.f * self.speed_feet + t_temp / self.s; // 6) Acceleration along fuselage // Atmospheric constants let rho = 0.0023769; // slugs/ft³ at sea level let q = 0.5 * rho * self.speed_feet.powi(2); // dynamic pressure // Approximate angle of attack from pitch angle (forward_tilt) let aoa = self.forward_tilt; // Clamp CL to critical AoA let cl = if aoa.abs() > self.critical_aoa_rad { self.cl_max * (1.0 - ((aoa.abs() - self.critical_aoa_rad) / self.critical_aoa_rad).min(1.0)) } else { self.cl_max * aoa / self.critical_aoa_rad }; // Drag = q * S * (cd0 + cl² / (π * AR)) let induced_drag = cl.powi(2) / (std::f64::consts::PI * self.aspect_ratio); // Reduce drag coefficient to make it more realistic - small planes have less drag let drag_coefficient = self.cd0 + induced_drag * 0.5; // Apply 50% factor to induced drag let drag_force = q * self.s * drag_coefficient; // Use the configured thrust directly instead of calculating from power // Scale with throttle (range 0-10) let throttle_ratio = (self.speed / 10.0).min(1.0).max(0.0); // Use direct thrust with throttle control let thrust_force = self.thrust_lbf * throttle_ratio * self.engine_count as f64; // We can also calculate additional thrust from power for more realistic engine modeling // Power = Force * Velocity, so Force = Power / Velocity let power_thrust = if self.speed_feet > 20.0 { // Only apply at reasonable speeds self.power_hp * 550.0 * throttle_ratio / self.speed_feet } else { 0.0 // Avoid division by small numbers }; // Use the greater of the two thrust values - at low speed use configured thrust, // at higher speeds might transition to power-based thrust let thrust_force = thrust_force.max(power_thrust); // Gravity force in body X (forward) direction let gravity_force = -self.mass_slugs * self.gravity_accel * self.forward_tilt.sin(); // Net force = thrust - drag + gravity component let net_force = thrust_force - drag_force + gravity_force; // F = ma ⇒ a = F / m let accel = net_force / self.mass_slugs; // 7) Speed update self.speed_feet += accel * PHYSICS_DT; if self.speed_feet < 0.1 { self.speed_feet = 0.1; } // 8) Next‐step states let a = 2.63 / self.speed_feet * self.d; let m_momentum = self.m; // ← accumulate momentum from step 1 self.x += (self.d * self.speed_feet - self.t / self.s * (0.19 * self.e + a * 0.64 + self.up_down / 1e3) - m_momentum * self.v // now matches banks.c’s M * v + self.gravity_accel * self.r33) * PHYSICS_DT; self.w = self.d; self.d += self.t * (0.45 - 14.0 / self.speed_feet * self.x - a * 130.0 - self.up_down * 0.14) * PHYSICS_DT / 125e2 + self.f * PHYSICS_DT * self.v; let d_param = self.v / self.speed_feet * 15.0; // 9) Turn rate P self.p = (self.t * (47.0 * self.i - local_intermediate * 52.0 + self.e * 94.0 * d_param - t_temp * 0.38 + self.left_right * 0.21 * self.e) / 1e2 + self.w * 179.0 * self.v) / 2312.0; // 10) Sideslip v self.v -= (self.w * self.f - self.t * (0.63 * local_intermediate - self.i * 0.086 + local_intermediate * self.e * 19.0 - d_param * 25.0 - 0.11 * self.left_right) / 107e2) * PHYSICS_DT; } }
Mark as private (unlisted)
for 30 minutes
for 6 hours
for 1 day
for 1 week
for 1 month
for 1 year
everlasting (like CCCP)