protected static float computeTorqueRatio(Vector3 lever, Vector3 thrustDir, Vector3 specTorque, float mass, Vector3 MoI, float ratio_factor, out float specificThrustToCom) { specificThrustToCom = Vector3.Dot(lever.normalized, thrustDir); var specificLinearAccel = Mathf.Abs(specificThrustToCom / mass); var specificAngularAccel = TorqueProps.AngularAcceleration(specTorque, MoI).magnitude; var ratio = specificLinearAccel > 0 ? Mathf.Clamp01(1 - 1 / (1 + specificAngularAccel * ratio_factor / specificLinearAccel)) : 0; return(ratio); }
public bool OptimizeForZeroTorque(Vector3 MoI) { var torque = Vector3.zero; float torque_error, angle_error, max_limit; if (Balanced.Count > 0) { torque = TorqueProps.CalculateImbalance(true, Manual, UnBalanced); EngineOptimizer.OptimizeLimitsForTorque(Balanced, Vector3.zero, torque, MoI, true, out max_limit, out torque_error, out angle_error); } if (Steering.Count > 0) { torque = TorqueProps.CalculateImbalance(true, Manual, UnBalanced, Balanced); return(EngineOptimizer.OptimizeLimitsForTorque(Steering, Vector3.zero, torque, MoI, true, out max_limit, out torque_error, out angle_error)); } return(true); }
void UpdateShipStats() { MinLimit = 0; var thrust = Vector3.zero; var selected_parts = GetSelectedParts(); CalculateMassAndCoM(selected_parts); if (CFG != null && CFG.Enabled && Engines.Count > 0) { ActiveEngines.Clear(); for (int i = 0, EnginesCount = Engines.Count; i < EnginesCount; i++) { var e = Engines[i]; var ecfg = CFG.ActiveProfile.GetConfig(e); if (ecfg == null || ecfg.On) { ActiveEngines.Add(e); } } if (selected_parts.Count > 0) { var selected_engines = new List <EngineWrapper>(); selected_parts.ForEach(p => find_engines_recursively(p, selected_engines)); ActiveEngines.AddRange(selected_engines); } if (ActiveEngines.Count > 0) { ActiveEngines.ForEach(process_active_engine); compute_inertia_tensor(); selected_parts.ForEach(update_inertia_tensor); ActiveEngines.SortByRole(); float max_limit, torque_error, angle_error; var imbalance = TorqueProps.CalculateImbalance(true, ActiveEngines.Manual, ActiveEngines.UnBalanced); if (ActiveEngines.Balanced.Count > 0) { EngineOptimizer.OptimizeLimitsForTorque(ActiveEngines.Balanced, Vector3.zero, imbalance, MoI, true, out max_limit, out torque_error, out angle_error); imbalance = TorqueProps.CalculateImbalance(true, ActiveEngines.Manual, ActiveEngines.UnBalanced, ActiveEngines.Balanced); } if (ActiveEngines.Steering.Count > 0) { if (!EngineOptimizer.OptimizeLimitsForTorque(ActiveEngines.Steering, Vector3.zero, imbalance, MoI, true, out max_limit, out torque_error, out angle_error)) { ActiveEngines.Steering.ForEach(e => e.limit = 0); ActiveEngines.Balanced.ForEach(e => e.limit = 0); } } MinLimit = 1; for (int i = 0, ActiveEnginesCount = ActiveEngines.Count; i < ActiveEnginesCount; i++) { var e = ActiveEngines[i]; thrust += e.wThrustDir * e.nominalCurrentThrust(e.limit); if (e.Role != TCARole.MANUAL && e.Role != TCARole.MANEUVER && MinLimit > e.limit) { MinLimit = e.limit; } e.forceThrustPercentage(e.limit * 100); } var T = thrust.magnitude / Utils.G0; MinTWR = T / Mass; MaxTWR = T / DryMass; } } }
public static bool OptimizeLimitsForTorque(IList <EngineWrapper> engines, Vector3 needed_torque, Vector3 start_imbalance, Vector3 MoI, bool useDefTorque, out float max_limit, out float torque_error, out float angle_error) { var num_engines = engines.Count; var zero_torque = needed_torque.IsZero(); var preset_limits = engines.Any(e => e.preset_limit >= 0); float error, angle; var last_error = -1f; Vector3 cur_imbalance = start_imbalance; Vector3 target; torque_error = -1f; angle_error = -1f; // Utils.Log("=============================== Optimization ===============================\n" + // "needed torque {}\n" + // "start imbalance {}\n" + // "MoI {}\n" + // "engines:\n{}", // needed_torque, start_imbalance, MoI, engines);//debug for (int i = 0; i < C.MaxIterations; i++) { //calculate current errors and target cur_imbalance = start_imbalance; for (int j = 0; j < num_engines; j++) { var e = engines[j]; cur_imbalance += e.Torque(e.throttle * e.limit, useDefTorque); } angle = zero_torque ? 0f : Utils.Angle2(cur_imbalance, needed_torque); target = needed_torque - cur_imbalance; error = TorqueProps.AngularAcceleration(target, MoI).magnitude; // Utils.Log("current imbalance: {}\nerror: {} < {}", cur_imbalance, error, ENG.OptimizationTorqueCutoff*ENG.OptimizationPrecision);//debug //remember the best state if (angle <= 0 && error < torque_error || angle + error < angle_error + torque_error || angle_error < 0) { for (int j = 0; j < num_engines; j++) { var e = engines[j]; e.best_limit = e.limit; } angle_error = angle; torque_error = error; } //check convergence conditions if (error < C.OptimizationTorqueCutoff * C.OptimizationPrecision || last_error > 0 && Mathf.Abs(error - last_error) < C.OptimizationPrecision * last_error) { break; } last_error = error; //normalize limits of main and balanced engines before optimization if (!preset_limits) { var limit_norm = 0f; for (int j = 0; j < num_engines; j++) { var e = engines[j]; if (e.Role == TCARole.MANEUVER) { continue; } if (limit_norm < e.limit) { limit_norm = e.limit; } } if (limit_norm > 0) { for (int j = 0; j < num_engines; j++) { var e = engines[j]; if (e.Role == TCARole.MANEUVER) { continue; } e.limit = Mathf.Clamp01(e.limit / limit_norm); } } } //optimize limits if (!optimization_for_torque_pass(engines, num_engines, target, target.magnitude, C.OptimizationPrecision, useDefTorque)) { break; } } var optimized = torque_error < C.OptimizationTorqueCutoff || (!zero_torque && angle_error < C.OptimizationAngleCutoff); // Utils.Log("num engines {}, optimized {}, TorqueError {}, TorqueAngle {}\nneeded torque {}\ncurrent turque {}\nlimits:\n{}\n" + // "-------------------------------------------------------------------------------------------------", // num_engines, optimized, torque_error, angle_error, needed_torque, cur_imbalance, // engines.Aggregate("", (s, e) => s+string.Format("{0}: VSF {1:P1}, throttle {2:P1}, best limit {3:P1}\n", // e.name, e.VSF, e.throttle, e.best_limit)));//debug //treat single-engine crafts specially if (num_engines == 1) { engines[0].limit = optimized ? engines[0].best_limit : 0f; max_limit = engines[0].limit; } else //restore the best state { max_limit = 0; for (int j = 0; j < num_engines; j++) { var e = engines[j]; e.limit = e.best_limit; if (e.limit > max_limit) { max_limit = e.limit; } } } return(optimized); }
public static bool OptimizeLimitsForTorque( IList <EngineWrapper> engines, Vector3 needed_torque, Vector3 start_imbalance, Vector3 MoI, bool useDefTorque, out float max_limit, out float torque_error, out float angle_error ) { torque_error = -1f; angle_error = -1f; max_limit = 0; var num_engines = engines.Count; if (num_engines == 0) { return(true); } var zero_torque = needed_torque.IsZero(); var preset_limits = engines.Any(e => e.preset_limit >= 0); var torqueOnly = !preset_limits && engines.All(e => e.Role == TCARole.MANEUVER); var last_error = -1f; for (var i = 0; i < C.MaxIterations; i++) { // calculate current target var cur_imbalance = start_imbalance; for (var j = 0; j < num_engines; j++) { var e = engines[j]; cur_imbalance += e.Torque(e.throttle * e.limit, useDefTorque); } var target = needed_torque - cur_imbalance; if (target.IsZero()) { break; } // calculate torque and angle errors var error = TorqueProps.AngularAcceleration(target, MoI).sqrMagnitude; var angle = zero_torque ? 0f : Utils.Angle2Rad(cur_imbalance, needed_torque) * C.AngleErrorWeight; //remember the best state if (zero_torque && error < torque_error || angle + error < angle_error + torque_error || angle_error < 0) { for (var j = 0; j < num_engines; j++) { var e = engines[j]; e.best_limit = e.limit; } torque_error = error; if (!zero_torque && !cur_imbalance.IsZero()) { angle_error = angle; } } // check convergence conditions if (error < C.TorqueCutoff || last_error > 0 && Mathf.Abs(error - last_error) < C.OptimizationPrecision * last_error) { break; } last_error = error; // normalize limits of main and balanced engines before optimization if (!preset_limits) { var limit_norm = 0f; for (var j = 0; j < num_engines; j++) { var e = engines[j]; if (e.Role == TCARole.MANEUVER) { continue; } if (limit_norm < e.limit) { limit_norm = e.limit; } } if (limit_norm > 0 && limit_norm < 1) { for (var j = 0; j < num_engines; j++) { var e = engines[j]; if (e.Role == TCARole.MANEUVER) { continue; } e.limit = Mathf.Clamp01(e.limit / limit_norm); } } } // optimize limits if (!optimization_for_torque_pass(engines, num_engines, target, target.magnitude, C.OptimizationPrecision, useDefTorque, torqueOnly)) { break; } } var optimized = torque_error < C.OptimizationTorqueCutoff || (angle_error >= 0 && angle_error < C.OptimizationAngleCutoff); // treat single-engine crafts specially if (num_engines == 1) { engines[0].limit = optimized ? engines[0].best_limit : 0f; max_limit = engines[0].limit; } else //restore the best state { max_limit = 0; for (var j = 0; j < num_engines; j++) { var e = engines[j]; e.limit = e.best_limit; if (e.limit > max_limit) { max_limit = e.limit; } } } return(optimized); }