/// <summary> /// Get the largest potential torque provided by the part, in Newton meters in the pitch, yaw and roll axes. /// Returns a pair of torques: the positive torque around each axis, and the negative torque. /// </summary> public static Torque GetPotentialTorque(this ITorqueProvider torqueProvider) { Vector3 pos; Vector3 neg; torqueProvider.GetPotentialTorque(out pos, out neg); Vector3d posd = pos; Vector3d negd = neg; return(new Torque(posd * 1000.0d, negd * 1000.0d)); }
/// <summary> /// Get the total torque for a vessel. /// </summary> /// <param name="vessel">The vessel from which ot get the total torque.</param> /// <returns>The vessel torque as a Vector3.</returns> public static Vector3 GetVesselTorque(Vessel vessel) { // the resulting torque Vector3 vesselTorque = Vector3.zero; // positive and negative vessel torque for all part modules that are torque providers Vector3 positiveTorque = Vector3.zero; Vector3 negativeTorque = Vector3.zero; // cycle through all vessel parts. int partCount = vessel.Parts.Count; for (int iPart = 0; iPart < partCount; ++iPart) { Part part = vessel.Parts[iPart]; // loop through all modules for the part int moduleCount = part.Modules.Count; for (int iModule = 0; iModule < moduleCount; ++iModule) { // find modules in part that are torque providers. ITorqueProvider torqueProvider = part.Modules[iModule] as ITorqueProvider; if (torqueProvider == null) { continue; } // pos and neg torque for this part module Vector3 posTorque; Vector3 negTorque; // get potential torque for the current module and update pos and neg torques. torqueProvider.GetPotentialTorque(out posTorque, out negTorque); positiveTorque += posTorque; negativeTorque += negTorque; } } // get max torque from all components of pos and neg torques. vesselTorque.x = Mathf.Max(positiveTorque.x, negativeTorque.x); vesselTorque.y = Mathf.Max(positiveTorque.y, negativeTorque.y); vesselTorque.z = Mathf.Max(positiveTorque.z, negativeTorque.z); return(vesselTorque); }
private void AnalyzeOtherTorque() { torqueOthers.Reset(); /* this is a special list of ITorqueProvider-containing Parts that are *NOT* Engines, RW, RCS, Control Surfaces */ foreach (var pair in otherTorque) { Part p = pair.Key; List <ITorqueProvider> mlist = p.Modules.GetModules <ITorqueProvider>(); for (int m = 0; m < mlist.Count; m++) { Vector3 pos; Vector3 neg; ITorqueProvider it = mlist[m]; it.GetPotentialTorque(out pos, out neg); torqueOthers.Add(pos); torqueOthers.Add(-neg); } } }
/// <summary> /// See https://github.com/KSP-KOS/KOS/issues/2814 for why this wrapper around KSP's API call exists. /// <para /> /// </summary> void CorrectedGetPotentialTorque(ITorqueProvider tp, out Vector3 pos, out Vector3 neg) { if (tp is ModuleRCS) { // The stock call GetPotentialTorque is completely broken in the case of ModuleRCS. So // this replaces it entirely until KSP ever fixes the bug that's been in their // bug list forever (probably won't get fixed). ModuleRCS rcs = tp as ModuleRCS; Part p = rcs.part; // This is the list of various reasons this RCS module might // be suppressed right now. It would be nice if all this // stuff flipped one common flag during Update for all the // rest of the code to check, but sadly that doesn't seem to // be the case and you have to check these things individually: if (p.ShieldedFromAirstream || !rcs.rcsEnabled || !rcs.isEnabled || rcs.isJustForShow || rcs.flameout || !rcs.rcs_active) { pos = new Vector3(0f, 0f, 0f); neg = new Vector3(0f, 0f, 0f); } else { // The algorithm here is adapted from code in the MandatoryRCS mod // that had to solve this same problem: // Note the swapping of Y and Z axes to align with "part space": Vector3 rotateEnables = new Vector3(rcs.enablePitch ? 1 : 0, rcs.enableRoll ? 1 : 0, rcs.enableYaw ? 1 : 0); Vector3 translateEnables = new Vector3(rcs.enableX ? 1 : 0, rcs.enableZ ? 1 : 0, rcs.enableY ? 1 : 0); pos = new Vector3(0f, 0f, 0f); neg = new Vector3(0f, 0f, 0f); for (int i = rcs.thrusterTransforms.Count - 1; i >= 0; --i) { Transform rcsTransform = rcs.thrusterTransforms[i]; Vector3 rcsPosFromCoM = rcsTransform.position - Vessel.CurrentCoM; Vector3 rcsThrustDir = rcs.useZaxis ? -rcsTransform.forward : rcsTransform.up; float powerFactor = rcs.thrusterPower * rcs.thrustPercentage * 0.01f; // Normally you'd check for precision mode to nerf powerFactor here, // but kOS doesn't obey that. Vector3 thrust = powerFactor * rcsThrustDir; Vector3 torque = Vector3d.Cross(rcsPosFromCoM, thrust); Vector3 transformedTorque = Vector3.Scale(Vessel.ReferenceTransform.InverseTransformDirection(torque), rotateEnables); pos += Vector3.Max(transformedTorque, Vector3.zero); neg += Vector3.Min(transformedTorque, Vector3.zero); } } } else if (tp is ModuleReactionWheel) { // Although ModuleReactionWheel *mostly* works, the stock version ignores // the authority limiter slider. It would have been possible to just take // the result it gives and multiply it by the slider, but that relies on // stock KSP never fixing it themselves and thus kOS would end up double- // applying that multiplitation. To avoid that, it seems better to just // make the entire thing homemade from scratch for now so if KSP ever fixes it // on their end that doesn't break it on kOS's end: ModuleReactionWheel wheel = tp as ModuleReactionWheel; if (!wheel.moduleIsEnabled || wheel.wheelState != ModuleReactionWheel.WheelState.Active || wheel.actuatorModeCycle == 2) { pos = new Vector3(0f, 0f, 0f); neg = new Vector3(0f, 0f, 0f); } else { float nerf = wheel.authorityLimiter / 100f; pos = new Vector3(nerf * wheel.PitchTorque, nerf * wheel.RollTorque, nerf * wheel.YawTorque); neg = -1 * pos; } } else { tp.GetPotentialTorque(out pos, out neg); } }
/// <summary> /// Import from MechJeb2 /// https://github.com/MuMech/MechJeb2/blob/dev/MechJeb2/VesselState.cs /// </summary> public static void AnalyzeParts(Vessel vessel) { torqueAvailable = Vector3d.zero; Vector6 torqueReactionSpeed6 = new Vector6(); torqueReactionWheel.Reset(); torqueControlSurface.Reset(); torqueGimbal.Reset(); torqueOthers.Reset(); UpdateRCSThrustAndTorque(vessel); for (int i = 0; i < vessel.parts.Count; i++) { Part p = vessel.parts[i]; for (int m = 0; m < p.Modules.Count; m++) { PartModule pm = p.Modules[m]; if (!pm.isEnabled) { continue; } ModuleReactionWheel rw = pm as ModuleReactionWheel; if (rw != null) { Vector3 pos; Vector3 neg; rw.GetPotentialTorque(out pos, out neg); // GetPotentialTorque reports the same value for pos & neg on ModuleReactionWheel torqueReactionWheel.Add(pos); torqueReactionWheel.Add(-neg); } else if (pm is ModuleControlSurface) // also does ModuleAeroSurface { ModuleControlSurface cs = (pm as ModuleControlSurface); Vector3 ctrlTorquePos; Vector3 ctrlTorqueNeg; cs.GetPotentialTorque(out ctrlTorquePos, out ctrlTorqueNeg); torqueControlSurface.Add(ctrlTorquePos); torqueControlSurface.Add(ctrlTorqueNeg); torqueReactionSpeed6.Add(Mathf.Abs(cs.ctrlSurfaceRange) / cs.actuatorSpeed * Vector3d.Max(ctrlTorquePos.Abs(), ctrlTorqueNeg.Abs())); } else if (pm is ModuleGimbal) { ModuleGimbal g = (pm as ModuleGimbal); Vector3 pos; Vector3 neg; g.GetPotentialTorque(out pos, out neg); // GetPotentialTorque reports the same value for pos & neg on ModuleGimbal torqueGimbal.Add(pos); torqueGimbal.Add(-neg); if (g.useGimbalResponseSpeed) { torqueReactionSpeed6.Add((Mathf.Abs(g.gimbalRange) / g.gimbalResponseSpeed) * Vector3d.Max(pos.Abs(), neg.Abs())); } } else if (pm is ModuleRCS) { // Handled separately } else if (pm is ITorqueProvider) { ITorqueProvider tp = pm as ITorqueProvider; Vector3 pos; Vector3 neg; tp.GetPotentialTorque(out pos, out neg); torqueOthers.Add(pos); torqueOthers.Add(neg); } } } torqueAvailable += Vector3d.Max(torqueReactionWheel.positive, torqueReactionWheel.negative); torqueAvailable += Vector3d.Max(rcsTorqueAvailable.positive, rcsTorqueAvailable.negative); torqueAvailable += Vector3d.Max(torqueControlSurface.positive, torqueControlSurface.negative); torqueAvailable += Vector3d.Max(torqueGimbal.positive, torqueGimbal.negative); torqueAvailable += Vector3d.Max(torqueOthers.positive, torqueOthers.negative); if (torqueAvailable.sqrMagnitude > 0) { torqueReactionSpeed = Vector3d.Max(torqueReactionSpeed6.positive, torqueReactionSpeed6.negative); torqueReactionSpeed.Scale(torqueAvailable.InvertNoNaN()); } else { torqueReactionSpeed = Vector3d.zero; } }