From 131e46a8a85d5c1dc3c2d525837e1f53ed8f2da8 Mon Sep 17 00:00:00 2001 From: Dunbaratu Date: Tue, 26 May 2020 03:43:22 -0500 Subject: [PATCH 01/11] allow CKAN to install on KSP 1.9.1 --- Resources/GameData/kOS/kOS.version | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/GameData/kOS/kOS.version b/Resources/GameData/kOS/kOS.version index 71c6d28099..e0452b2450 100644 --- a/Resources/GameData/kOS/kOS.version +++ b/Resources/GameData/kOS/kOS.version @@ -26,7 +26,7 @@ }, "KSP_VERSION_MAX": { "MAJOR": 1, - "MINOR": 8, - "PATCH": 99 + "MINOR": 9, + "PATCH": 1 } } From 68add601bdc6d507f3e73af28217b9781dee5e80 Mon Sep 17 00:00:00 2001 From: Max Maton Date: Fri, 19 Feb 2021 00:27:36 +0100 Subject: [PATCH 02/11] Add ReactionWheel partmodule handling --- .../structures/vessels/reactionwheel.rst | 76 +++++++++++++++++++ src/kOS/Function/BuildList.cs | 1 + src/kOS/Function/PrintList.cs | 25 ++++++ src/kOS/Suffixed/Part/ReactionWheelValue.cs | 56 ++++++++++++++ src/kOS/Suffixed/VesselTarget.Parts.cs | 7 ++ src/kOS/Utilities/VesselUtils.cs | 4 + src/kOS/kOS.csproj | 1 + 7 files changed, 170 insertions(+) create mode 100644 doc/source/structures/vessels/reactionwheel.rst create mode 100644 src/kOS/Suffixed/Part/ReactionWheelValue.cs diff --git a/doc/source/structures/vessels/reactionwheel.rst b/doc/source/structures/vessels/reactionwheel.rst new file mode 100644 index 0000000000..1462d7a3a7 --- /dev/null +++ b/doc/source/structures/vessels/reactionwheel.rst @@ -0,0 +1,76 @@ +.. _reactionwheel: + +Reaction Wheel +====== + +Some of the Parts returned by :ref:`LIST PARTS ` will be a Reaction Wheel. It is also possible to get just the Reaction Wheel parts by executing ``LIST ReactionWheels``, for example:: + + LIST ReactionWheels IN myVariable. + FOR wheel IN myVariable { + print "A reaction wheel exists that is currently " + wheel:WHEELSTATE. + }. + +.. structure:: Reaction Wheel + + .. list-table:: + :header-rows: 1 + :widths: 1 1 2 + + * - Suffix + - Type (units) + - Description + + * - All suffixes of :struct:`Part` + - + - :struct:`RCS` objects are a type of :struct:`Part` + + + * - :attr:`AUTHORITYLIMITER` + - :ref:`scalar ` (%) + - The authority limit for the reaction wheel. + * - :attr:`MAXTORQUE` + - :ref:`Vector ` (kN) + - A vector representing the maximum amount of force that can be applied over the Pitch, Yaw and Roll axis. + * - :attr:`WHEELSTATE` + - :ref:`String ` + - The status of the reaction wheel: ACTIVE, DISABLED or BROKEN. + * - :attr:`TORQUERESPONSESPEED` + - :ref:`scalar ` + - + +.. note:: + + Reaction wheels always apply their torque at the ships center of mass. The center of mass of a ship can be found using the :struct:`Vessel `:position parameter. + +.. note:: + + A :struct:`ReactionWheel` is a type of :struct:`Part`, and therefore can use all the suffixes of :struct:`Part`. + +.. attribute:: ReactionWheel:AUTHORITYLIMITER + + :access: Get/Set + :type: :ref:`scalar ` (%) + + The authority limit of the reaction wheel. + +.. attribute:: ReactionWheel:MAXTORQUE + + :access: Get + :type: :ref:`Vector ` + + A vector representing the amount of force that can be applied over the three axis: V(maxPitchTorque, maxYawTorque, maxRollTorque). + +.. attribute:: ReactionWheel:WHEELSTATE + + :access: Get + :type: :ref:`String ` + + The status of the reaction wheel. One of: ACTIVE, DISABLED or BROKEN. + +.. attribute:: ReactionWheel:TORQUERESPONSESPEED + + :access: Get + :type: :ref:`scalar ` + + Unknown + diff --git a/src/kOS/Function/BuildList.cs b/src/kOS/Function/BuildList.cs index 84376baff6..900275de7e 100644 --- a/src/kOS/Function/BuildList.cs +++ b/src/kOS/Function/BuildList.cs @@ -36,6 +36,7 @@ public override void Execute(SharedObjects shared) case "parts": case "engines": case "rcs": + case "reactionwheels": case "sensors": case "elements": case "dockingports": diff --git a/src/kOS/Function/PrintList.cs b/src/kOS/Function/PrintList.cs index 0667db142c..52ef5850e3 100644 --- a/src/kOS/Function/PrintList.cs +++ b/src/kOS/Function/PrintList.cs @@ -66,6 +66,10 @@ public override void Execute(SharedObjects shared) list = GetRCSList(shared); break; + case "reactionwheels": + list = GetReactionWheelList(shared); + break; + case "sensors": list = GetSensorList(shared); break; @@ -292,6 +296,27 @@ private kList GetRCSList(SharedObjects shared) return list; } + private kList GetReactionWheelList(SharedObjects shared) + { + var list = new kList(); + list.AddColumn("ID", 12, ColumnAlignment.Left); + list.AddColumn("Name", 28, ColumnAlignment.Left); + + foreach (Part part in shared.Vessel.Parts) + { + foreach (PartModule module in part.Modules) + { + var wheel = module as ModuleReactionWheel; + if (wheel != null) + { + list.AddItem(part.ConstructID(), part.partInfo.name); + } + } + } + + return list; + } + private kList GetSensorList(SharedObjects shared) { var list = new kList(); diff --git a/src/kOS/Suffixed/Part/ReactionWheelValue.cs b/src/kOS/Suffixed/Part/ReactionWheelValue.cs new file mode 100644 index 0000000000..6238b10309 --- /dev/null +++ b/src/kOS/Suffixed/Part/ReactionWheelValue.cs @@ -0,0 +1,56 @@ +using kOS.Safe; +using kOS.Safe.Encapsulation; +using kOS.Safe.Encapsulation.Suffixes; +using kOS.Safe.Exceptions; +using kOS.Safe.Utilities; +using kOS.Suffixed.PartModuleField; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEngine; +using System; + +namespace kOS.Suffixed.Part +{ + [kOS.Safe.Utilities.KOSNomenclature("ReactionWheel")] + public class ReactionWheelValue : PartValue + { + private readonly ModuleReactionWheel module; + + /// + /// Do not call! VesselTarget.ConstructPart uses this, would use `friend VesselTarget` if this was C++! + /// + internal ReactionWheelValue(SharedObjects shared, global::Part part, PartValue parent, DecouplerValue decoupler, ModuleReactionWheel module) + : base(shared, part, parent, decoupler) + { + this.module = module; + RegisterInitializer(RCSInitializeSuffixes); + } + private void RCSInitializeSuffixes() + { + AddSuffix("AUTHORITYLIMITER", new SetSuffix(() => module.authorityLimiter, value => module.authorityLimiter = Math.Max(Math.Min(value, 0), 100), "Sets the authority limiter for this reaction wheel.")); + AddSuffix("WHEELSTATE", new Suffix(() => module.wheelState.ToString().ToUpper(), "The status of the reaction wheel: ACTIVE, DISABLED or BROKEN.")); + AddSuffix("MAXTORQUE", new Suffix(() => new Vector(module.PitchTorque, module.YawTorque, module.RollTorque), "A vector representing max torque force over (pitch, yaw, roll).")); + AddSuffix("TORQUERESPONSESPEED", new Suffix(() => module.torqueResponseSpeed)); + } + + public static ListValue PartsToList(IEnumerable parts, SharedObjects sharedObj) + { + var toReturn = new ListValue(); + var vessel = VesselTarget.CreateOrGetExisting(sharedObj); + foreach (var part in parts) + { + foreach (var module in part.Modules) + { + if (module is ModuleReactionWheel) + { + toReturn.Add(vessel[part]); + // Only add each part once + break; + } + } + } + return toReturn; + } + } +} diff --git a/src/kOS/Suffixed/VesselTarget.Parts.cs b/src/kOS/Suffixed/VesselTarget.Parts.cs index b1a6f76465..a6692295d3 100644 --- a/src/kOS/Suffixed/VesselTarget.Parts.cs +++ b/src/kOS/Suffixed/VesselTarget.Parts.cs @@ -118,6 +118,7 @@ private void ConstructPart(global::Part part, PartValue parent, DecouplerValue d // gather all potential modules and then select from those valid. IEngineStatus engine = null; ModuleRCS rcs = null; + ModuleReactionWheel torqueWheel = null; PartValue separator = null; ModuleEnviroSensor sensor = null; @@ -131,6 +132,10 @@ private void ConstructPart(global::Part part, PartValue parent, DecouplerValue d { rcs = module as ModuleRCS; } + else if (module is ModuleReactionWheel) + { + torqueWheel = module as ModuleReactionWheel; + } else if (module is IStageSeparator) { var dock = module as ModuleDockingNode; @@ -177,6 +182,8 @@ private void ConstructPart(global::Part part, PartValue parent, DecouplerValue d self = new EngineValue(Shared, part, parent, decoupler); else if (rcs != null) self = new RCSValue(Shared, part, parent, decoupler, rcs); + else if (torqueWheel != null) + self = new ReactionWheelValue(Shared, part, parent, decoupler, torqueWheel); else if (separator != null) self = separator; else if (sensor != null) diff --git a/src/kOS/Utilities/VesselUtils.cs b/src/kOS/Utilities/VesselUtils.cs index 6d28b7012d..5f492222bb 100644 --- a/src/kOS/Utilities/VesselUtils.cs +++ b/src/kOS/Utilities/VesselUtils.cs @@ -86,6 +86,10 @@ public static ListValue PartList(this IShipconstruct vessel, string partType, Sh list = RCSValue.PartsToList(partList, sharedObj); break; + case "REACTIONWHEELS": + list = ReactionWheelValue.PartsToList(partList, sharedObj); + break; + case "SENSORS": list = SensorValue.PartsToList(partList, sharedObj); break; diff --git a/src/kOS/kOS.csproj b/src/kOS/kOS.csproj index 59bea5c557..a634897a0d 100644 --- a/src/kOS/kOS.csproj +++ b/src/kOS/kOS.csproj @@ -172,6 +172,7 @@ + From a7919ca6dcde05412b4b088d3a89606101aa2403 Mon Sep 17 00:00:00 2001 From: Max Maton Date: Fri, 19 Feb 2021 18:33:44 +0100 Subject: [PATCH 03/11] WIP for review --- src/kOS/Suffixed/AttitudeController.cs | 155 +++++++++++++++++++ src/kOS/Suffixed/AttitudeCorrectionResult.cs | 16 ++ 2 files changed, 171 insertions(+) create mode 100644 src/kOS/Suffixed/AttitudeController.cs create mode 100644 src/kOS/Suffixed/AttitudeCorrectionResult.cs diff --git a/src/kOS/Suffixed/AttitudeController.cs b/src/kOS/Suffixed/AttitudeController.cs new file mode 100644 index 0000000000..de0737b5ab --- /dev/null +++ b/src/kOS/Suffixed/AttitudeController.cs @@ -0,0 +1,155 @@ +using kOS.Safe; +using kOS.Safe.Encapsulation; +using kOS.Safe.Encapsulation.Suffixes; +using kOS.Suffixed.PartModuleField; +using kOS.Suffixed.Part; +using UnityEngine; +using System; +using Expansions.Serenity; + +namespace kOS.Suffixed +{ + [kOS.Safe.Utilities.KOSNomenclature("AttitudeController")] + class AttitudeController : Structure + { + public AttitudeController(PartValue part, PartModuleFields module, ITorqueProvider provider) + { + torqueProvider = provider; + + AddSuffix("PART", new Suffix(() => part, "The part this AttitudeController belongs to.")); + AddSuffix("MODULE", new Suffix(() => module, "The module this AttitudeController belongs to.")); + AddSuffix("ALLOWPITCH", new SetSuffix(() => allowPitch, (v) => { allowPitch = v; }, "Should this attitude controller respond to pitch input?")); + AddSuffix("ALLOWROLL", new SetSuffix(() => allowRoll, (v) => { allowRoll = v; }, "Should this attitude controller respond to roll input?")); + AddSuffix("ALLOWYAW", new SetSuffix(() => allowYaw, (v) => { allowYaw = v; }, "Should this attitude controller respond to yaw input?")); + AddSuffix("ALLOWX", new SetSuffix(() => allowX, (v) => { allowX = v; }, "Should this attitude controller respond to translation X input?")); + AddSuffix("ALLOWY", new SetSuffix(() => allowY, (v) => { allowY = v; }, "Should this attitude controller respond to translation Y input?")); + AddSuffix("ALLOWZ", new SetSuffix(() => allowZ, (v) => { allowZ = v; }, "Should this attitude controller respond to translation Z input?")); + + AddSuffix("HASCUSTOMTHROTTLE", new Suffix(() => hasCustomThrottle, "Does this attitude controller have an independent custom throttle?")); + AddSuffix("CUSTOMTHROTTLE", new SetSuffix(() => customThrottle, (v) => { customThrottle = v; }, "The value of the independent custom throttle.")); + AddSuffix("CONTROLLERTYPE", new Suffix(() => controllerType, "The type of controller.")); + AddSuffix("STATUS", new Suffix(() => status, "The status of the controller. One of UNKNOWN, ACTIVE, INACTIVE, BROKEN")); + + AddSuffix("RESPONSETIME", new Suffix(() => responseTime, "The response speed of the controller.")); + } + //void test() + //{ + // ModuleControlSurface a; + // ModuleRCS a; + // ModuleEngines a; + // ModuleReactionWheel a; + // Expansions.Serenity.ModuleRoboticServoRotor a; + // ModuleResourceDrain a; + //} + protected ITorqueProvider torqueProvider { get; private set; } + public virtual bool allowPitch { get { return false; } set { } } + public virtual bool allowRoll { get { return false; } set { } } + public virtual bool allowYaw { get { return false; } set { } } + public virtual bool allowX { get { return false; } set { } } + public virtual bool allowY { get { return false; } set { } } + public virtual bool allowZ { get { return false; } set { } } + + public virtual float rotationAuthorityLimiter { get { return 0; } set { } } + public virtual float translationAuthorityLimiter { get { return 0; } set { } } + + public virtual bool hasCustomThrottle { get { return false; } } + public virtual float customThrottle { get { return 0; } set { } } + public virtual string controllerType { get { return "UNKNOWN"; } } + public virtual string status { get { return "UNKNOWN"; } } + public virtual float responseTime { get { return 0; } set { } } + + public virtual AttitudeCorrectionResult positiveRotation + { + get + { + Vector3 pos = Vector3.zero; + Vector3 _ = Vector3.zero; + torqueProvider.GetPotentialTorque(out pos, out _); + return new AttitudeCorrectionResult(new Vector(pos), Vector.Zero); + } + } + public virtual AttitudeCorrectionResult negativeRotation + { + get + { + Vector3 _ = Vector3.zero; + Vector3 neg = Vector3.zero; + torqueProvider.GetPotentialTorque(out _, out neg); + return new AttitudeCorrectionResult(new Vector(neg), Vector.Zero); + } + } + public virtual AttitudeCorrectionResult throttle { get { return new AttitudeCorrectionResult(Vector.Zero, Vector.Zero); } } + } + + class AttitudeControllerReactionWheel : AttitudeController + { + public AttitudeControllerReactionWheel(PartValue part, PartModuleFields module, ModuleReactionWheel wheel) + :base(part, module, wheel) + { + this.wheel = wheel; + } + + protected ModuleReactionWheel wheel { get; private set; } + + public override string controllerType { get { return "REACTIONWHEEL"; } } + public override string status { get { return wheel.State.ToString().ToUpper(); } } + public override float rotationAuthorityLimiter { get { return wheel.authorityLimiter; } set { wheel.authorityLimiter = Math.Max(Math.Min(value, 100), 0); } } + public override bool allowPitch { get { return true; } set { } } + public override bool allowRoll { get { return true; } set { } } + public override bool allowYaw { get { return true; } set { } } + public override float responseTime { get { return wheel.torqueResponseSpeed; } set { } } + } + + class AttitudeControllerControlSurface : AttitudeController + { + public AttitudeControllerControlSurface(PartValue part, PartModuleFields module, ModuleControlSurface surface) + :base(part, module, surface) + { + this.surface = surface; + } + + protected ModuleControlSurface surface { get; private set; } + + public override string controllerType { get { return "CONTROLSURFACE"; } } + public override bool allowPitch { get { return !surface.ignorePitch; } set { surface.ignorePitch = !value; } } + public override bool allowRoll { get { return !surface.ignoreRoll; } set { surface.ignoreRoll = !value; } } + public override bool allowYaw { get { return !surface.ignoreYaw; } set { surface.ignoreYaw = !value; } } + public override float rotationAuthorityLimiter { get { return surface.authorityLimiter; } set { surface.authorityLimiter = Math.Max(Math.Min(value, 100), 0); } } + } + + // This assumes the rotor is connected with the base connected to the controller, not the rotating end. + class AttitudeControllerRotor : AttitudeController + { + public AttitudeControllerRotor(PartValue part, PartModuleFields module, ModuleRoboticServoRotor rotor) + : base(part, module, null) + { + this.rotor = rotor; + } + + protected ModuleRoboticServoRotor rotor { get; private set; } + + public override string controllerType { get { return "ROTOR"; } } + public override AttitudeCorrectionResult positiveRotation { get { return new AttitudeCorrectionResult(Vector.Zero, Vector.Zero); } } + public override AttitudeCorrectionResult negativeRotation { get { return new AttitudeCorrectionResult(Vector.Zero, Vector.Zero); } } + public override AttitudeCorrectionResult throttle + { + get + { + if (!rotor.servoIsMotorized) + return new AttitudeCorrectionResult(Vector.Zero, Vector.Zero); + + Vector3 rotationEffect = Vector3.zero; + Vector3 + //TODO: compute rotation effect on yaw, pitch, roll + if (rotor.rotateCounterClockwise) + rotationEffect *= -1.0f; + rotationEffect *= rotor.servoMotorSize; + return new AttitudeCorrectionResult(new Vector(rotationEffect), Vector.Zero); + } + } + public override bool hasCustomThrottle { get { return true; } } + public override float customThrottle { get { return rotor.maxTorque; } set { rotor.maxTorque = Math.Max(Math.Min(value, 100), 0); } } + public override string status { get { return rotor.motorState.ToUpper(); } } + public override float responseTime { get { return rotor.rotorSpoolTime; } set { } } + } +} diff --git a/src/kOS/Suffixed/AttitudeCorrectionResult.cs b/src/kOS/Suffixed/AttitudeCorrectionResult.cs new file mode 100644 index 0000000000..013eba1137 --- /dev/null +++ b/src/kOS/Suffixed/AttitudeCorrectionResult.cs @@ -0,0 +1,16 @@ +using kOS.Safe; +using kOS.Safe.Encapsulation; +using kOS.Safe.Encapsulation.Suffixes; + +namespace kOS.Suffixed +{ + [kOS.Safe.Utilities.KOSNomenclature("AttitudeCorrectionResult")] + class AttitudeCorrectionResult : Structure + { + public AttitudeCorrectionResult(Vector torque, Vector translation) + { + AddSuffix("TORQUE", new Suffix(() => torque, "The torque for this correction.")); + AddSuffix("TRANSLATION", new Suffix(() => translation, "The translation force for this correction.")); + } + } +} From 70cb4d64cbcf376a8bf8d37fe80643a040069dee Mon Sep 17 00:00:00 2001 From: Max Maton Date: Sun, 21 Feb 2021 03:10:43 +0100 Subject: [PATCH 04/11] Add generalized attitude control interface --- .../structures/vessels/attitudecontroller.rst | 221 +++++++ doc/source/structures/vessels/part.rst | 8 + .../structures/vessels/reactionwheel.rst | 76 --- doc/source/structures/vessels/vessel.rst | 12 + src/kOS/Control/SteeringManager.cs | 12 +- src/kOS/Suffixed/AttitudeController.cs | 567 ++++++++++++++++-- src/kOS/Suffixed/AttitudeCorrectionResult.cs | 19 +- src/kOS/Suffixed/Part/PartValue.cs | 21 + src/kOS/Suffixed/Part/ReactionWheelValue.cs | 56 -- src/kOS/Suffixed/VesselTarget.Parts.cs | 7 - src/kOS/Suffixed/VesselTarget.cs | 16 + src/kOS/Utilities/VesselUtils.cs | 4 - src/kOS/kOS.csproj | 2 + 13 files changed, 839 insertions(+), 182 deletions(-) create mode 100644 doc/source/structures/vessels/attitudecontroller.rst delete mode 100644 doc/source/structures/vessels/reactionwheel.rst delete mode 100644 src/kOS/Suffixed/Part/ReactionWheelValue.cs diff --git a/doc/source/structures/vessels/attitudecontroller.rst b/doc/source/structures/vessels/attitudecontroller.rst new file mode 100644 index 0000000000..2e1dc4f06a --- /dev/null +++ b/doc/source/structures/vessels/attitudecontroller.rst @@ -0,0 +1,221 @@ +.. _attitudecontroller: + +Attitude Controller +====== + +A ship usually has various attitude controllers like control surfaces, engines, RCS thrusters, rotors and drain valves. These controllers can be configured as follows: + + local controllers to ship:AttitudeControllers. + print(controllers[0]:ControllerType + " allows pitch: " + controllers[0]:allowPitch). + +.. structure:: AttitudeController + + .. list-table:: Members + :header-rows: 1 + :widths: 1 1 2 + + * - Suffix + - Type (units) + - Description + + * - :attr:`PART` + - :struct:`Part ` + - The part this controller belongs to. + * - :attr:`MODULE` + - :struct:`PartModule ` + - The module this controller belongs to. Will return false if there is no matching module. + * - :attr:`ALLOWPITCH` + - :ref:`Boolean ` + - Gets or sets wheter this controller should respond to pitch input. + * - :attr:`ALLOWYAW` + - :ref:`Boolean ` + - Gets or sets wheter this controller should respond to yaw input. + * - :attr:`ALLOWROLL` + - :ref:`Boolean ` + - Gets or sets wheter this controller should respond to roll input. + * - :attr:`ALLOWX` + - :ref:`Boolean ` + - Gets or sets wheter this controller should respond to x translation input. + * - :attr:`ALLOWY` + - :ref:`Boolean ` + - Gets or sets wheter this controller should respond to y translation input. + * - :attr:`ALLOWZ` + - :ref:`Boolean ` + - Gets or sets wheter this controller should respond to z translation input. + * - :attr:`HASCUSTOMTHROTTLE` + - :ref:`Boolean ` + - Wheter this controller has a custom throttle input. + * - :attr:`CUSTOMTHROTTLE` + - :ref:`scalar ` (%) + - The value the custom throttle. + * - :attr:`CONTROLLERTYPE` + - :ref:`string ` + - The type of the controller. + * - :attr:`STATUS` + - :ref:`string ` + - A string indicating more detailed status about the controller if available. + * - :attr:`RESPONSETIME` + - :ref:`scalar ` + - The reported responsetime of the controller. + * - :attr:`POSITIVEROTATION` + - :struct:`AttitudeCorrectionResult ` + - What is expected to happen when you provide a positive value to pitch, yaw, roll. + * - :attr:`NEGATIVEROTATION` + - :struct:`AttitudeCorrectionResult ` + - What is expected to happen when you provide a negative value to pitch, yaw, roll. + * - :meth:`RESPONSEFOR` + - :struct:`AttitudeCorrectionResult ` + - What is expected to happen for arbitrary combinations of pitch, yaw, roll, translate x, translate y, translate z, custom throttle. + + +.. note:: + + The rotation responses are simplified models of reality and are likely to be off to various degrees. + + + +.. _attitudecontroller_PART: + +.. attribute:: AttitudeController:PART + + :access: Get + :type: :struct:`Part ` + + The part this controller belongs to. + +.. _attitudecontroller_MODULE: + +.. attribute:: AttitudeController:MODULE + + :access: Get only + :type: :struct:`PartModule ` + + The module this controller belongs to. Will return false if there is no matching module. + +.. _attitudecontroller_ALLOWPITCH: + +.. attribute:: AttitudeController:ALLOWPITCH + + :access: Get/Set + :type: :ref:`boolean ` + + Determines whether this controller is allowed to respond to pitch input. + +.. _attitudecontroller_ALLOWYAW: + +.. attribute:: AttitudeController:ALLOWYAW + + :access: Get/Set + :type: :ref:`boolean ` + + Determines whether this controller is allowed to respond to yaw input. + +.. _attitudecontroller_ALLOWROLL: + +.. attribute:: AttitudeController:ALLOWROLL + + :access: Get/Set + :type: :ref:`boolean ` + + Determines whether this controller is allowed to respond to roll input. + +.. _attitudecontroller_ALLOWX: + +.. attribute:: AttitudeController:ALLOWX + + :access: Get/Set + :type: :ref:`boolean ` + + Determines whether this controller is allowed to respond to translation X input. + +.. _attitudecontroller_ALLOWY: + +.. attribute:: AttitudeController:ALLOWY + + :access: Get/Set + :type: :ref:`boolean ` + + Determines whether this controller is allowed to respond to translation Y input. + +.. _attitudecontroller_ALLOWZ: + +.. attribute:: AttitudeController:ALLOWZ + + :access: Get/Set + :type: :ref:`boolean ` + + Determines whether this controller is allowed to respond to translation Z input. + +.. _attitudecontroller_HASCUSTOMTHROTTLE: + +.. attribute:: AttitudeController:HASCUSTOMTHROTTLE` + + :access: Get only + :type: :ref:`boolean ` + + Returns true if this controller has a custom throttle you can modify. + +.. _attitudecontroller_CUSTOMTHROTTLE: + +.. attribute:: AttitudeController:CUSTOMTHROTTLE + + :access: Get/Set + :type: :ref:`scalar ` (%) + + Sets the custom throttle for this controller. + +.. _attitudecontroller_CONTROLLERTYPE: + +.. attribute:: AttitudeController:CONTROLLERTYPE + + :access: Get only + :type: :ref:`string ` + + The type of the attitude controller (ENGINE, DRAINVALVE, ROTOR, RCS, REACTIONWHEEL) or UNKNOWN if the exact type is unknown. + +.. _attitudecontroller_STATUS: + +.. attribute:: AttitudeController:STATUS + + :access: Get only + :type: :ref:`string ` + + The status of the controller if known. UNKNOWN otherwise. + +.. _attitudecontroller_RESPONSETIME: + +.. attribute:: AttitudeController:RESPONSETIME + + :access: Get only + :type: :ref:`scalar ` + + The reported response time of this controller. + +.. _attitudecontroller_POSITIVEROTATION: + +.. attribute:: AttitudeController:POSITIVEROTATION + + :access: Get only + :type: :struct:`AttitudeCorrectionResult ` + + What is expected to happen when you provide a positive value to pitch, yaw, roll. + +.. _attitudecontroller_NEGATIVEROTATION: + +.. attribute:: AttitudeController:NEGATIVEROTATION + + :access: Get only + :type: :struct:`AttitudeCorrectionResult ` + + What is expected to happen when you provide a negative value to pitch, yaw, roll. + +.. _attitudecontroller_RESPONSEFOR: + +.. method:: AttitudeController:RESPONSEFOR(pitchYawRollInput, translateXYZInput, throttle) + + :parameter pitchYawRollInput: A vector describing user pitch, yaw, roll input between -1 and 1. + :parameter translateXYZInput: A vector describing user x, y, z translation input between -1 and 1. + :parameter throttle: A scalar representing the custom throttle value in percent. + :type: :struct:`AttitudeCorrectionResult ` + + Simulates the effect of the given input on the ship. This allows computing things like RCS thruster inbalances. diff --git a/doc/source/structures/vessels/part.rst b/doc/source/structures/vessels/part.rst index 867890266a..55820dae86 100644 --- a/doc/source/structures/vessels/part.rst +++ b/doc/source/structures/vessels/part.rst @@ -144,6 +144,9 @@ These are the generic properties every PART has. You can obtain a list of values * - :meth:`ALLTAGGEDPARTS` - :struct:`List` (of :struct:`Part`) - Search the branch from here down for all parts with a non-blank tag name. + * - :meth:`ATTITUDECONTROLLERS` + - :struct:`List` (of :struct:`AttitudeController`) + - List of Attitude Controllers in this part. .. attribute:: Part:NAME @@ -609,3 +612,8 @@ These are the generic properties every PART has. You can obtain a list of values branch of the vessel's part tree from the current part down through its children and its children's children and so on. +.. method:: Vessel::ATTITUDECONTROLLERS() + + :return: :struct:`List` of :struct:`AttitudeController` objects + + Return all Attitude Controllers in this part. \ No newline at end of file diff --git a/doc/source/structures/vessels/reactionwheel.rst b/doc/source/structures/vessels/reactionwheel.rst deleted file mode 100644 index 1462d7a3a7..0000000000 --- a/doc/source/structures/vessels/reactionwheel.rst +++ /dev/null @@ -1,76 +0,0 @@ -.. _reactionwheel: - -Reaction Wheel -====== - -Some of the Parts returned by :ref:`LIST PARTS ` will be a Reaction Wheel. It is also possible to get just the Reaction Wheel parts by executing ``LIST ReactionWheels``, for example:: - - LIST ReactionWheels IN myVariable. - FOR wheel IN myVariable { - print "A reaction wheel exists that is currently " + wheel:WHEELSTATE. - }. - -.. structure:: Reaction Wheel - - .. list-table:: - :header-rows: 1 - :widths: 1 1 2 - - * - Suffix - - Type (units) - - Description - - * - All suffixes of :struct:`Part` - - - - :struct:`RCS` objects are a type of :struct:`Part` - - - * - :attr:`AUTHORITYLIMITER` - - :ref:`scalar ` (%) - - The authority limit for the reaction wheel. - * - :attr:`MAXTORQUE` - - :ref:`Vector ` (kN) - - A vector representing the maximum amount of force that can be applied over the Pitch, Yaw and Roll axis. - * - :attr:`WHEELSTATE` - - :ref:`String ` - - The status of the reaction wheel: ACTIVE, DISABLED or BROKEN. - * - :attr:`TORQUERESPONSESPEED` - - :ref:`scalar ` - - - -.. note:: - - Reaction wheels always apply their torque at the ships center of mass. The center of mass of a ship can be found using the :struct:`Vessel `:position parameter. - -.. note:: - - A :struct:`ReactionWheel` is a type of :struct:`Part`, and therefore can use all the suffixes of :struct:`Part`. - -.. attribute:: ReactionWheel:AUTHORITYLIMITER - - :access: Get/Set - :type: :ref:`scalar ` (%) - - The authority limit of the reaction wheel. - -.. attribute:: ReactionWheel:MAXTORQUE - - :access: Get - :type: :ref:`Vector ` - - A vector representing the amount of force that can be applied over the three axis: V(maxPitchTorque, maxYawTorque, maxRollTorque). - -.. attribute:: ReactionWheel:WHEELSTATE - - :access: Get - :type: :ref:`String ` - - The status of the reaction wheel. One of: ACTIVE, DISABLED or BROKEN. - -.. attribute:: ReactionWheel:TORQUERESPONSESPEED - - :access: Get - :type: :ref:`scalar ` - - Unknown - diff --git a/doc/source/structures/vessels/vessel.rst b/doc/source/structures/vessels/vessel.rst index 4601150e77..96a079ab13 100644 --- a/doc/source/structures/vessels/vessel.rst +++ b/doc/source/structures/vessels/vessel.rst @@ -56,6 +56,7 @@ Vessels are also :ref:`Orbitable`, and as such have all the associate :attr:`SIZECLASS` :struct:`String` Return the size class for an asteroid-like object :attr:`ANGULARMOMENTUM` :struct:`Vector` In :ref:`SHIP_RAW ` :attr:`ANGULARVEL` :struct:`Vector` In :ref:`SHIP_RAW ` + :attr:`MOMENTOFINERTIA` :struct:`Vector` Moment of inertia over pitch, yaw, roll :attr:`SENSORS` :struct:`VesselSensors` Sensor data :attr:`LOADED` :struct:`Boolean` loaded into KSP physics engine or "on rails" :attr:`UNPACKED` :struct:`Boolean` The ship has individual parts unpacked @@ -80,6 +81,7 @@ Vessels are also :ref:`Orbitable`, and as such have all the associate :meth:`PARTSINGROUP(group)` :struct:`List` :struct:`Parts ` by action group :meth:`MODULESINGROUP(group)` :struct:`List` :struct:`PartModules ` by action group :meth:`ALLTAGGEDPARTS()` :struct:`List` :struct:`Parts ` that have non-blank nametags + :meth:`ATTITUDECONTROLLERS()` :struct:`List` :struct:`Attitude Controllers ` present on this ship. :attr:`CREWCAPACITY` :struct:`scalar` Crew capacity of this vessel :meth:`CREW()` :struct:`List` all :struct:`CrewMembers ` :attr:`CONNECTION` :struct:`Connection` Returns your connection to this vessel @@ -410,6 +412,10 @@ Vessels are also :ref:`Orbitable`, and as such have all the associate congruent with how VESSEL:ANGULARMOMENTUM is expressed, and for backward compatibility with older kOS scripts. +.. attribute:: Vessel:MOMENTOFINERTIA + + The moment of inertia of this ship over the pitch, yaw and roll axis. + .. attribute:: Vessel:SENSORS :type: :struct:`VesselSensors` @@ -604,6 +610,12 @@ Vessels are also :ref:`Orbitable`, and as such have all the associate Return all parts who's nametag isn't blank. For more information, see :ref:`ship parts and modules `. +.. method:: Vessel::ATTITUDECONTROLLERS() + + :return: :struct:`List` of :struct:`AttitudeController` objects + + Return all Attitude Controllers on this ship. + .. attribute:: Vessel:CREWCAPACITY :type: :ref:`scalar ` diff --git a/src/kOS/Control/SteeringManager.cs b/src/kOS/Control/SteeringManager.cs index 18e9bb1ff5..c26df257fe 100644 --- a/src/kOS/Control/SteeringManager.cs +++ b/src/kOS/Control/SteeringManager.cs @@ -592,7 +592,7 @@ public void UpdateStateVectors() // TODO: If stock vessel.MOI stops being so weird, we might be able to change the following line // into this instead. (See the comment on FindMOI()'s header): // momentOfInertia = shared.Vessel.MOI; - momentOfInertia = FindMoI(); + momentOfInertia = SteeringManager.FindMoI(shared.Vessel); adjustTorque = Vector3d.zero; measuredTorque = Vector3d.Scale(momentOfInertia, angularAcceleration); @@ -682,7 +682,7 @@ public void UpdateTorque() /// See https://github.com/KSP-KOS/KOS/issues/2814 for why this wrapper around KSP's API call exists. /// /// - void CorrectedGetPotentialTorque(ITorqueProvider tp, out Vector3 pos, out Vector3 neg) + public void CorrectedGetPotentialTorque(ITorqueProvider tp, out Vector3 pos, out Vector3 neg) { if (tp is ModuleRCS) { @@ -769,13 +769,17 @@ void CorrectedGetPotentialTorque(ITorqueProvider tp, out Vector3 pos, out Vector /// would expect "control from here" to do.) /// /// TODO: Check this again after each KSP stock release to see if it's been changed or not. - public Vector3 FindMoI() + public static Vector3 FindMoI(Vessel vessel) { + var vesselTransform = vessel.ReferenceTransform; + var vesselRotation = vesselTransform.rotation * Quaternion.Euler(-90, 0, 0); + var centerOfMass = vessel.CoMD; + var tensor = Matrix4x4.zero; Matrix4x4 partTensor = Matrix4x4.identity; Matrix4x4 inertiaMatrix = Matrix4x4.identity; Matrix4x4 productMatrix = Matrix4x4.identity; - foreach (var part in Vessel.Parts) + foreach (var part in vessel.Parts) { if (part.rb != null) { diff --git a/src/kOS/Suffixed/AttitudeController.cs b/src/kOS/Suffixed/AttitudeController.cs index de0737b5ab..5731f9c64e 100644 --- a/src/kOS/Suffixed/AttitudeController.cs +++ b/src/kOS/Suffixed/AttitudeController.cs @@ -1,23 +1,33 @@ -using kOS.Safe; +using Expansions.Serenity; +using kOS.Control; +using kOS.Module; using kOS.Safe.Encapsulation; using kOS.Safe.Encapsulation.Suffixes; -using kOS.Suffixed.PartModuleField; using kOS.Suffixed.Part; -using UnityEngine; +using kOS.Suffixed.PartModuleField; +using kOS.Utilities; +using System.Linq; +using System.Collections.Generic; using System; -using Expansions.Serenity; +using UnityEngine; namespace kOS.Suffixed { [kOS.Safe.Utilities.KOSNomenclature("AttitudeController")] - class AttitudeController : Structure + public class AttitudeController : Structure { public AttitudeController(PartValue part, PartModuleFields module, ITorqueProvider provider) { torqueProvider = provider; + this.part = part; AddSuffix("PART", new Suffix(() => part, "The part this AttitudeController belongs to.")); - AddSuffix("MODULE", new Suffix(() => module, "The module this AttitudeController belongs to.")); + AddSuffix("MODULE", new Suffix(() => + { + if (module == null) + return new BooleanValue(false); + return module; + }, "The module this AttitudeController belongs to.")); AddSuffix("ALLOWPITCH", new SetSuffix(() => allowPitch, (v) => { allowPitch = v; }, "Should this attitude controller respond to pitch input?")); AddSuffix("ALLOWROLL", new SetSuffix(() => allowRoll, (v) => { allowRoll = v; }, "Should this attitude controller respond to roll input?")); AddSuffix("ALLOWYAW", new SetSuffix(() => allowYaw, (v) => { allowYaw = v; }, "Should this attitude controller respond to yaw input?")); @@ -28,19 +38,41 @@ public AttitudeController(PartValue part, PartModuleFields module, ITorqueProvid AddSuffix("HASCUSTOMTHROTTLE", new Suffix(() => hasCustomThrottle, "Does this attitude controller have an independent custom throttle?")); AddSuffix("CUSTOMTHROTTLE", new SetSuffix(() => customThrottle, (v) => { customThrottle = v; }, "The value of the independent custom throttle.")); AddSuffix("CONTROLLERTYPE", new Suffix(() => controllerType, "The type of controller.")); - AddSuffix("STATUS", new Suffix(() => status, "The status of the controller. One of UNKNOWN, ACTIVE, INACTIVE, BROKEN")); + AddSuffix("STATUS", new Suffix(() => status, "The status of the controller.")); AddSuffix("RESPONSETIME", new Suffix(() => responseTime, "The response speed of the controller.")); + + AddSuffix("POSITIVEROTATION", new Suffix(() => positiveRotation, "What is the torque applied when giving a positive input on pitch, roll and yaw.")); + AddSuffix("NEGATIVEROTATION", new Suffix(() => negativeRotation, "What is the torque applied when giving a negative input on pitch, roll and yaw.")); + + AddSuffix("RESPONSEFOR", new ThreeArgsSuffix((rotation, translation, thrust) => + { + return GetResponseFor((float)rotation.X, (float)rotation.Y, (float)rotation.Z, (float)translation.X, (float)translation.Y, (float)translation.Z, thrust); + }, "What is the torque applied when setting the following controls.")); } - //void test() - //{ - // ModuleControlSurface a; - // ModuleRCS a; - // ModuleEngines a; - // ModuleReactionWheel a; - // Expansions.Serenity.ModuleRoboticServoRotor a; - // ModuleResourceDrain a; - //} + + public static AttitudeController FromModule(PartValue part, PartModuleFields moduleStructure, PartModule module) + { + if (module is ModuleReactionWheel) + return new AttitudeControllerReactionWheel(part, moduleStructure, (ModuleReactionWheel)module); + if (module is ModuleRCS) + return new AttitudeControllerRCS(part, moduleStructure, (ModuleRCS)module); + if (module is ModuleControlSurface) + return new AttitudeControllerControlSurface(part, moduleStructure, (ModuleControlSurface)module); + if (module is ModuleRoboticServoRotor) + return new AttitudeControllerRotor(part, moduleStructure, (ModuleRoboticServoRotor)module); + if (module is ModuleEngines) + return new AttitudeControllerEngine(part, moduleStructure); + if (module is ModuleResourceDrain) + return new AttitudeControllerDrainValve(part, moduleStructure, (ModuleResourceDrain)module); + if (module is ModuleGimbal) + return null; // Already covered by engine attitude controller + if (module is ITorqueProvider) + return new AttitudeController(part, moduleStructure, (ITorqueProvider)module); + return null; + } + + protected PartValue part { get; private set; } protected ITorqueProvider torqueProvider { get; private set; } public virtual bool allowPitch { get { return false; } set { } } public virtual bool allowRoll { get { return false; } set { } } @@ -58,14 +90,43 @@ public AttitudeController(PartValue part, PartModuleFields module, ITorqueProvid public virtual string status { get { return "UNKNOWN"; } } public virtual float responseTime { get { return 0; } set { } } + + public override string ToString() + { + return "AttitudeController(" + controllerType + ")"; + } + + protected void GetPotentialTorque(out Vector3 positive, out Vector3 negative) + { + var controlParameter = kOSVesselModule.GetInstance(part.Shared.Vessel).GetFlightControlParameter("steering"); + var steeringManager = controlParameter as SteeringManager; + + if (steeringManager == null) + { + positive = Vector3.zero; + negative = Vector3.zero; + return; + } + Vector3 pos = Vector3.zero; + Vector3 neg = Vector3.zero; + steeringManager.CorrectedGetPotentialTorque(torqueProvider, out pos, out neg); + positive.x = pos.x; + positive.y = pos.z; + positive.z = pos.y; + negative.x = neg.x; + negative.y = neg.z; + negative.z = neg.y; + } public virtual AttitudeCorrectionResult positiveRotation { get { Vector3 pos = Vector3.zero; Vector3 _ = Vector3.zero; - torqueProvider.GetPotentialTorque(out pos, out _); - return new AttitudeCorrectionResult(new Vector(pos), Vector.Zero); + GetPotentialTorque(out pos, out _); + + AttitudeCorrectionResult estimate = GetResponseFor(1, 1, 1, 0, 0, 0, customThrottle); + return new AttitudeCorrectionResult(new Vector(pos), estimate.translation); } } public virtual AttitudeCorrectionResult negativeRotation @@ -74,13 +135,85 @@ public virtual AttitudeCorrectionResult negativeRotation { Vector3 _ = Vector3.zero; Vector3 neg = Vector3.zero; - torqueProvider.GetPotentialTorque(out _, out neg); - return new AttitudeCorrectionResult(new Vector(neg), Vector.Zero); + GetPotentialTorque(out _, out neg); + + AttitudeCorrectionResult estimate = GetResponseFor(-1, -1, -1, 0, 0, 0, customThrottle); + return new AttitudeCorrectionResult(new Vector(neg), estimate.translation); } } - public virtual AttitudeCorrectionResult throttle { get { return new AttitudeCorrectionResult(Vector.Zero, Vector.Zero); } } + public virtual AttitudeCorrectionResult GetResponseFor(float pitch, float yaw, float roll, float x, float y, float z, float throttle) + { + return new AttitudeCorrectionResult(Vector.Zero, Vector.Zero); + } + + protected Vector3 StarVector { get { return VesselUtils.GetFacing(part.Shared.Vessel).Rotation * Vector3.right; } } + protected Vector3 TopVector { get { return VesselUtils.GetFacing(part.Shared.Vessel).Rotation * Vector3.up; } } + protected Vector3 ForeVector { get { return VesselUtils.GetFacing(part.Shared.Vessel).Rotation * Vector3.forward; } } + protected Vector3 PitchVector(Vector3 lever) + { + Vector3 projectedLever = Vector3.ProjectOnPlane(lever, StarVector); + if (projectedLever.sqrMagnitude < 0.0001) + return Vector3.zero; + return Vector3.Cross(projectedLever.normalized, StarVector); + } + protected Vector3 YawVector(Vector3 lever) + { + Vector3 projectedLever = Vector3.ProjectOnPlane(lever, TopVector); + if (projectedLever.sqrMagnitude < 0.0001) + return Vector3.zero; + return Vector3.Cross(projectedLever.normalized, TopVector); + } + protected Vector3 RollVector(Vector3 lever) + { + Vector3 projectedLever = Vector3.ProjectOnPlane(lever, ForeVector); + if (projectedLever.sqrMagnitude < 0.0001) + return Vector3.zero; + return Vector3.Cross(projectedLever.normalized, ForeVector); + } + protected Vector3 PositionToLever(Vector3 position) + { + Vector3 shipCenterOfMass = part.Shared.Vessel.CoMD; + return position - shipCenterOfMass; + } + public static Direction GetTransformFacing(Transform transform) + { + var rotation = transform.rotation; + Quaternion facing = Quaternion.Inverse(Quaternion.Euler(90, 0, 0) * Quaternion.Inverse(rotation) * Quaternion.identity); + return new Direction(facing); + } + + protected AttitudeCorrectionResult SplitComponents(Vector3 lever, Vector3 thrust) + { + Vector3 pitchVector = PitchVector(lever); + Vector3 yawVector = YawVector(lever); + Vector3 rollVector = RollVector(lever); + + Vector3 pitchLever = Vector3.ProjectOnPlane(lever, StarVector); + Vector3 yawLever = Vector3.ProjectOnPlane(lever, TopVector); + Vector3 rollLever = Vector3.ProjectOnPlane(lever, ForeVector); + + float resultingPitch = Vector3.Dot(thrust, pitchVector) * pitchLever.magnitude; + float resultingYaw = Vector3.Dot(thrust, yawVector) * yawLever.magnitude; + float resultingRoll = Vector3.Dot(thrust, rollVector) * rollLever.magnitude; + + // Whatever force not used for angular momentum is thrust + Vector3 remainingThrust = thrust; + if (pitchVector.magnitude > 0) + remainingThrust = Vector3.ProjectOnPlane(remainingThrust, pitchVector); + if (yawVector.magnitude > 0) + remainingThrust = Vector3.ProjectOnPlane(remainingThrust, yawVector); + if (rollVector.magnitude > 0) + remainingThrust = Vector3.ProjectOnPlane(remainingThrust, rollVector); + + float resultingX = Vector3.Dot(remainingThrust, StarVector); + float resultingY = Vector3.Dot(remainingThrust, TopVector); + float resultingZ = Vector3.Dot(remainingThrust, ForeVector); + + return new AttitudeCorrectionResult(new Vector(resultingPitch, resultingYaw, resultingRoll), new Vector(resultingX, resultingY, resultingZ)); + } } + [kOS.Safe.Utilities.KOSNomenclature("AttitudeController", KOSToCSharp = false)] class AttitudeControllerReactionWheel : AttitudeController { public AttitudeControllerReactionWheel(PartValue part, PartModuleFields module, ModuleReactionWheel wheel) @@ -98,8 +231,27 @@ public AttitudeControllerReactionWheel(PartValue part, PartModuleFields module, public override bool allowRoll { get { return true; } set { } } public override bool allowYaw { get { return true; } set { } } public override float responseTime { get { return wheel.torqueResponseSpeed; } set { } } + public override AttitudeCorrectionResult GetResponseFor(float pitch, float yaw, float roll, float x, float y, float z, float throttle) + { + Vector3 pos = Vector3.zero; + Vector3 neg = Vector3.zero; + GetPotentialTorque(out pos, out neg); + Vector3 rotation = Vector3.zero; + rotation += new Vector3( + Math.Max(0, pitch) * pos.x, + Math.Max(0, yaw) * pos.z, + Math.Max(0, roll) * pos.y + ); + rotation += new Vector3( + Math.Min(0, pitch) * -neg.x, + Math.Min(0, yaw) * -neg.z, + Math.Min(0, roll) * -neg.y + ); + return new AttitudeCorrectionResult(new Vector(rotation), new Vector(Vector.Zero)); + } } + [kOS.Safe.Utilities.KOSNomenclature("AttitudeController", KOSToCSharp = false)] class AttitudeControllerControlSurface : AttitudeController { public AttitudeControllerControlSurface(PartValue part, PartModuleFields module, ModuleControlSurface surface) @@ -115,9 +267,28 @@ public AttitudeControllerControlSurface(PartValue part, PartModuleFields module, public override bool allowRoll { get { return !surface.ignoreRoll; } set { surface.ignoreRoll = !value; } } public override bool allowYaw { get { return !surface.ignoreYaw; } set { surface.ignoreYaw = !value; } } public override float rotationAuthorityLimiter { get { return surface.authorityLimiter; } set { surface.authorityLimiter = Math.Max(Math.Min(value, 100), 0); } } + public override AttitudeCorrectionResult GetResponseFor(float pitch, float yaw, float roll, float x, float y, float z, float throttle) + { + Vector3 pos = Vector3.zero; + Vector3 neg = Vector3.zero; + GetPotentialTorque(out pos, out neg); + Vector3 rotation = Vector3.zero; + rotation += new Vector3( + Math.Max(0, pitch) * pos.x, + Math.Max(0, yaw) * pos.z, + Math.Max(0, roll) * pos.y + ); + rotation += new Vector3( + Math.Min(0, pitch) * -neg.x, + Math.Min(0, yaw) * -neg.z, + Math.Min(0, roll) * -neg.y + ); + return new AttitudeCorrectionResult(new Vector(rotation), new Vector(Vector.Zero)); + } } // This assumes the rotor is connected with the base connected to the controller, not the rotating end. + [kOS.Safe.Utilities.KOSNomenclature("AttitudeController", KOSToCSharp = false)] class AttitudeControllerRotor : AttitudeController { public AttitudeControllerRotor(PartValue part, PartModuleFields module, ModuleRoboticServoRotor rotor) @@ -131,25 +302,353 @@ public AttitudeControllerRotor(PartValue part, PartModuleFields module, ModuleRo public override string controllerType { get { return "ROTOR"; } } public override AttitudeCorrectionResult positiveRotation { get { return new AttitudeCorrectionResult(Vector.Zero, Vector.Zero); } } public override AttitudeCorrectionResult negativeRotation { get { return new AttitudeCorrectionResult(Vector.Zero, Vector.Zero); } } - public override AttitudeCorrectionResult throttle + public override bool hasCustomThrottle { get { return true; } } + public override float customThrottle { get { return rotor.maxTorque; } set { rotor.maxTorque = Math.Max(Math.Min(value, 100), 0); } } + public override string status { get { return rotor.motorState.ToUpper(); } } + public override float responseTime { get { return rotor.rotorSpoolTime; } set { } } + + public override AttitudeCorrectionResult GetResponseFor(float pitch, float yaw, float roll, float x, float y, float z, float throttle) + { + if (!rotor.servoIsMotorized) + return new AttitudeCorrectionResult(Vector.Zero, Vector.Zero); + // Likely makes too many assumptions about the axis direction but the rotation axis does not seem to be public. + Quaternion rotator = rotor.servoTransformRotation; + Vector3d facing = part.GetFacing().Vector; + Vector3 axis = new Vector3((float)facing.x, (float)facing.y, (float)facing.z); + float pitchAlignment = Vector3.Dot(axis, StarVector); + float yawAlignment = Vector3.Dot(axis, TopVector); + float rollAlignment = Vector3.Dot(axis, ForeVector); + Vector3 rotationEffect = new Vector3(pitchAlignment, yawAlignment, rollAlignment); + + if (rotor.rotateCounterClockwise) + rotationEffect *= -1.0f; + rotationEffect *= rotor.servoMotorSize; + rotationEffect *= throttle / 100; + return new AttitudeCorrectionResult(new Vector(rotationEffect), new Vector(0, 0, 0)); + } + } + + [kOS.Safe.Utilities.KOSNomenclature("AttitudeController", KOSToCSharp = false)] + class AttitudeControllerEngine : AttitudeController + { + public AttitudeControllerEngine(PartValue part, PartModuleFields module) + : base(part, module, null) + { + // This assumes that: + // - nozzles are controlled by at most one gimbal + // - even though multiple nozzles are controlled by one gimbal, they can rotate independently + // Gimbal assignment is not accurate but I can't find a good way to do it. It succesfully falls back to the first gimbal for now. + + var maxFound = new Dictionary(); + gimbals = part.Part.Modules.OfType().ToList(); + gimbalLinking = new Dictionary(); + foreach (var engine in part.Part.Modules.OfType()) + { + gimbalLinking.Add(engine, null); + maxFound[engine] = 0; + if (gimbals.Any()) // Assign first gimbal by default + gimbalLinking[engine] = gimbals.First(); + } + foreach (var gimbal in gimbals) + { + if (gimbal.engineMultsList == null) + continue; + foreach (var sublist in gimbal.engineMultsList) + { + if (sublist == null) + continue; + foreach (var kv in sublist) + { + var engine = kv.Key; + var strength = kv.Value; + if (strength < 0.01) + continue; + if (!gimbalLinking.ContainsKey(engine) || gimbalLinking[engine] == null) + { + gimbalLinking[engine] = gimbal; + maxFound[engine] = 0; + } + + if (maxFound[engine] < strength) + { + gimbalLinking[engine] = gimbal; + maxFound[engine] = strength; + } + } + } + } + } + protected List gimbals { get; private set; } + protected Dictionary gimbalLinking { get; private set; } + + public override bool allowPitch + { + get + { + foreach (var gimbal in gimbals) + if (gimbal.enablePitch) + return true; + return false; + } + set + { + foreach (var gimbal in gimbals) + gimbal.enablePitch = value; + } + } + public override bool allowRoll + { + get + { + foreach (var gimbal in gimbals) + if (gimbal.enableRoll) + return true; + return false; + } + set + { + foreach (var gimbal in gimbals) + gimbal.enableRoll = value; + } + } + public override bool allowYaw + { + get + { + foreach (var gimbal in gimbals) + if (gimbal.enableYaw) + return true; + return false; + } + set + { + foreach (var gimbal in gimbals) + gimbal.enableYaw = value; + } + } + public override AttitudeCorrectionResult positiveRotation { get { return GetResponseFor(1, 1, 1, 0, 0, 0, customThrottle); } } + public override AttitudeCorrectionResult negativeRotation { get { return GetResponseFor(-1, -1, -1, 0, 0, 0, customThrottle); } } + public override string controllerType { get { return "ENGINE"; } } + + public override bool hasCustomThrottle + { + get + { + var engines = gimbalLinking.Keys; + foreach (var engine in engines) + if (engine.independentThrottle) + return true; + return false; + } + } + public override float customThrottle + { + get + { + var engines = gimbalLinking.Keys; + if (engines.Count == 0) + return 0; + return engines.Select((e) => e.independentThrottlePercentage).Average(); + } + set + { + var engines = gimbalLinking.Keys; + foreach (var engine in engines) + engine.independentThrottlePercentage = Math.Max(Math.Min(value, 100), 0); + } + } + public override float responseTime { get { - if (!rotor.servoIsMotorized) - return new AttitudeCorrectionResult(Vector.Zero, Vector.Zero); + var engines = gimbalLinking.Keys; + if (engines.Count == 0) + return 0; + return engines.Select((e) => e.throttleResponseRate).Average(); + } + } + + public override AttitudeCorrectionResult GetResponseFor(float pitch, float yaw, float roll, float x, float y, float z, float throttle) + { + // Assumes that engines burn to Facing * gimbal + // Assumes that gimbals have equal freedom in both axis + + var engines = gimbalLinking.Keys; + if (engines.Count == 0) + return new AttitudeCorrectionResult(new Vector(0, 0, 0), new Vector(0, 0, 0)); + Vector3d facing3d = part.GetFacing().Vector; + Vector3 facing = new Vector3((float)facing3d.x, (float)facing3d.y, (float)facing3d.z); + + + float shipThrottle = (float)kOSVesselModule.GetInstance(part.Shared.Vessel).GetFlightControlParameter("throttle").GetValue(); + + var result = new AttitudeCorrectionResult(new Vector(0, 0, 0), new Vector(0, 0, 0)); + + foreach (var engine in engines) + { + if (engine.thrustTransforms.Count == 0) + continue; + + float engineThrottle = throttle / 100; + if (!engine.independentThrottle) + throttle = shipThrottle; + + float thrust = engine.GetThrust(null, true, engineThrottle); + // I assume this is supposed to be normalized + float multiplierSum = engine.thrustTransformMultipliers.Sum(); + if (multiplierSum < 0.01) + multiplierSum = 1; // Handle all zero multipliers, assume equal thrust + float thrustPerNozzle = thrust / engine.thrustTransforms.Count; + + + for (int i = 0; i < engine.thrustTransforms.Count; i++) + { + float scaledMultiplier = engine.thrustTransformMultipliers[i] / multiplierSum; + if (scaledMultiplier < 0.01) + scaledMultiplier = 1; // Handle all zero multipliers, assume equal thrust + float nozzleThrust = thrustPerNozzle * scaledMultiplier; + + Vector3 thrustPosition = engine.thrustTransforms[i].position; + + Vector3 thrustVector = facing; + Vector3 lever = PositionToLever(thrustPosition); + Vector3 pitchVector = PitchVector(lever); + Vector3 yawVector = YawVector(lever); + Vector3 rollVector = RollVector(lever); - Vector3 rotationEffect = Vector3.zero; - Vector3 - //TODO: compute rotation effect on yaw, pitch, roll - if (rotor.rotateCounterClockwise) - rotationEffect *= -1.0f; - rotationEffect *= rotor.servoMotorSize; - return new AttitudeCorrectionResult(new Vector(rotationEffect), Vector.Zero); + if (gimbalLinking[engine] != null) + { + var gimbal = gimbalLinking[engine]; + + float maskedPitch = gimbal.enablePitch ? pitch : 0; + float maskedYaw = gimbal.enableYaw ? yaw : 0; + float maskedRoll = gimbal.enableRoll ? roll : 0; + Vector3 desiredDirection = maskedPitch * pitchVector + maskedYaw * yawVector + maskedRoll * rollVector; + if (desiredDirection.magnitude > 0.01) + desiredDirection = desiredDirection.normalized; + + Vector3 desiredGimbalResponse = Vector3.ProjectOnPlane(desiredDirection, facing); + float rangeDegrees = gimbal.gimbalRange * gimbal.gimbalLimiter / 100; + float maxDeflection = (float)Math.Tan(rangeDegrees / 180 * Math.PI); + if (desiredGimbalResponse.magnitude > maxDeflection) + { + desiredGimbalResponse = desiredGimbalResponse.normalized * maxDeflection; + } + thrustVector = (desiredGimbalResponse + facing * (1 - desiredGimbalResponse.magnitude)).normalized; //readd main engine thrust + } + thrustVector *= nozzleThrust; + + result += SplitComponents(lever, thrustVector); + } } + + return result; + } + } + + [kOS.Safe.Utilities.KOSNomenclature("AttitudeController", KOSToCSharp = false)] + class AttitudeControllerDrainValve : AttitudeController + { + public AttitudeControllerDrainValve(PartValue part, PartModuleFields module, ModuleResourceDrain drain) + : base(part, module, null) + { + this.drain = drain; + } + + protected ModuleResourceDrain drain { get; private set; } + + public override string controllerType { get { return "DRAIN"; } } + public override AttitudeCorrectionResult positiveRotation { get { return new AttitudeCorrectionResult(Vector.Zero, Vector.Zero); } } + public override AttitudeCorrectionResult negativeRotation { get { return new AttitudeCorrectionResult(Vector.Zero, Vector.Zero); } } + public override AttitudeCorrectionResult GetResponseFor(float pitch, float yaw, float roll, float x, float y, float z, float throttle) + { + // Assumes thrust comes from the part position + // Assumes all resources are drained (not always true) + + float ISP = 5.0f; // All resources in ResourcesGeneric.cfg have a drain ISP of 5. + var parent = part.Parent; + float weightDelta = parent.Part.GetWetMass() - parent.Part.GetDryMass(); + float fuelFlow = weightDelta / 5; // Max throttle (20%) drains tank in 5 seconds + + float throttleFraction = Math.Min(Math.Max(throttle, 0), 20) / 20; + float thrust = fuelFlow * ISP * 9.80665f * throttleFraction; + + Vector3d facing = part.GetFacing().Vector; + Vector3 lever = PositionToLever(part.Part.transform.position); + + return SplitComponents(lever, facing * thrust); } public override bool hasCustomThrottle { get { return true; } } - public override float customThrottle { get { return rotor.maxTorque; } set { rotor.maxTorque = Math.Max(Math.Min(value, 100), 0); } } - public override string status { get { return rotor.motorState.ToUpper(); } } - public override float responseTime { get { return rotor.rotorSpoolTime; } set { } } + public override float customThrottle + { + get { return drain.drainRate; } + set { drain.drainRate = Math.Min(Math.Max(value, 0), 20); } + } + } + + [kOS.Safe.Utilities.KOSNomenclature("AttitudeController", KOSToCSharp = false)] + class AttitudeControllerRCS : AttitudeController + { + public AttitudeControllerRCS(PartValue part, PartModuleFields module, ModuleRCS rcs) + : base(part, module, rcs) + { + this.rcs = rcs; + } + + protected ModuleRCS rcs { get; private set; } + + public override string controllerType { get { return "RCS"; } } + public override bool allowPitch { get { return rcs.enablePitch; } set { rcs.enablePitch = value; } } + public override bool allowRoll { get { return rcs.enableRoll; } set { rcs.enableRoll = value; } } + public override bool allowYaw { get { return rcs.enableYaw; } set { rcs.enableYaw = value; } } + public override bool allowX { get { return rcs.enableX; } set { rcs.enableX = value; } } + public override bool allowY { get { return rcs.enableY; } set { rcs.enableY = value; } } + public override bool allowZ { get { return rcs.enableZ; } set { rcs.enableZ = value; } } + public override AttitudeCorrectionResult GetResponseFor(float pitch, float yaw, float roll, float x, float y, float z, float throttle) + { + var result = new AttitudeCorrectionResult(new Vector(0, 0, 0), new Vector(0, 0, 0)); + if (part.Part.ShieldedFromAirstream || !rcs.rcsEnabled || !rcs.isEnabled || + rcs.isJustForShow || rcs.flameout || !rcs.rcs_active) + return result; + + var maskedPitch = rcs.enablePitch ? pitch : 0; + var maskedYaw = rcs.enableYaw ? yaw : 0; + var maskedRoll = rcs.enableRoll ? roll : 0; + var maskedX = rcs.enableX ? x : 0; + var maskedY = rcs.enableY ? y : 0; + var maskedZ = rcs.enableZ ? z : 0; + + foreach (var transform in rcs.thrusterTransforms) + { + Vector3 rcsPosFromCoM = transform.position - part.Part.vessel.CurrentCoM; + Vector3 rcsThrustDir = rcs.useZaxis ? -transform.forward : transform.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 translation = rcsPosFromCoM.normalized * (float)Vector3d.Dot(rcsPosFromCoM.normalized, thrust); + Vector3 transformedTorque = part.Part.vessel.ReferenceTransform.InverseTransformDirection(torque); + Vector3 transformedTranslation = part.Part.vessel.ReferenceTransform.InverseTransformDirection(translation); + + Vector3 desiredTorque = new Vector3(maskedPitch, maskedRoll, maskedYaw); + Vector3 desiredTranslation = new Vector3(maskedX, maskedY, maskedZ); + + // Big assumption here about how ksp prioritizes conflicing inputs + float alignment = Vector3.Dot(transformedTorque.normalized, desiredTorque) + + Vector3.Dot(transformedTranslation.normalized, desiredTranslation); + + // This simulates a bang-bang rcs, which makes it work for the positiveRotation case and the V(0,0,0) case. + // KSP seems to use partial thrust though, unsure how to model this. + if (alignment > 0) + { + result += new AttitudeCorrectionResult( + new Vector(transformedTorque.x, transformedTorque.z, transformedTorque.y), + new Vector(transformedTranslation.x, transformedTranslation.z, transformedTranslation.y) + ); + } + } + return result; + } } } diff --git a/src/kOS/Suffixed/AttitudeCorrectionResult.cs b/src/kOS/Suffixed/AttitudeCorrectionResult.cs index 013eba1137..6b85bf8cb4 100644 --- a/src/kOS/Suffixed/AttitudeCorrectionResult.cs +++ b/src/kOS/Suffixed/AttitudeCorrectionResult.cs @@ -1,16 +1,33 @@ using kOS.Safe; using kOS.Safe.Encapsulation; using kOS.Safe.Encapsulation.Suffixes; +using System; namespace kOS.Suffixed { [kOS.Safe.Utilities.KOSNomenclature("AttitudeCorrectionResult")] - class AttitudeCorrectionResult : Structure + public class AttitudeCorrectionResult : Structure { public AttitudeCorrectionResult(Vector torque, Vector translation) { + this.torque = torque; + this.translation = translation; + AddSuffix("TORQUE", new Suffix(() => torque, "The torque for this correction.")); AddSuffix("TRANSLATION", new Suffix(() => translation, "The translation force for this correction.")); } + + public Vector torque { get; private set; } + public Vector translation { get; private set; } + + public override string ToString() + { + return String.Format("AttitudeCorrectionResult(torque: {0}, translation: {1})", torque.ToString(), translation.ToString()); + } + + public static AttitudeCorrectionResult operator +(AttitudeCorrectionResult a, AttitudeCorrectionResult b) + { + return new AttitudeCorrectionResult(a.torque + b.torque, a.translation + b.translation); + } } } diff --git a/src/kOS/Suffixed/Part/PartValue.cs b/src/kOS/Suffixed/Part/PartValue.cs index 310156bc2b..4fb17bb24b 100644 --- a/src/kOS/Suffixed/Part/PartValue.cs +++ b/src/kOS/Suffixed/Part/PartValue.cs @@ -83,6 +83,7 @@ private void PartInitializeSuffixes() AddSuffix("PARTSTAGGED", new OneArgsSuffix(GetPartsTagged)); AddSuffix("PARTSTAGGEDPATTERN", new OneArgsSuffix(GetPartsTaggedPattern)); AddSuffix("ALLTAGGEDPARTS", new NoArgsSuffix(GetAllTaggedParts)); + AddSuffix("ATTITUDECONTROLLERS", new NoArgsSuffix(() => new ListValue(GetAttitudeControllers()))); } public BoundsValue GetBoundsValue() @@ -362,6 +363,26 @@ public ListValue GetAllTaggedParts() .Any(tag => !String.Equals(tag.nameTag, "", StringComparison.CurrentCultureIgnoreCase))), Shared); } + public IEnumerable GetAttitudeControllers() + { + var result = new List(); + bool foundEngine = false; + foreach (PartModule module in Part.Modules) + { + if (module is ModuleEngines) + { + if (foundEngine) + continue; + foundEngine = true; + } + PartModuleFields moduleStructure = PartModuleFieldsFactory.Construct(module, Shared); + var controller = AttitudeController.FromModule(this, moduleStructure, module); + if (controller != null) + result.Add(controller); + } + return result; + } + /// /// Return all the PartModules matching the condition given, in the parts /// tree starting from this part downward (this branch of the vessel parts tree) diff --git a/src/kOS/Suffixed/Part/ReactionWheelValue.cs b/src/kOS/Suffixed/Part/ReactionWheelValue.cs deleted file mode 100644 index 6238b10309..0000000000 --- a/src/kOS/Suffixed/Part/ReactionWheelValue.cs +++ /dev/null @@ -1,56 +0,0 @@ -using kOS.Safe; -using kOS.Safe.Encapsulation; -using kOS.Safe.Encapsulation.Suffixes; -using kOS.Safe.Exceptions; -using kOS.Safe.Utilities; -using kOS.Suffixed.PartModuleField; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using UnityEngine; -using System; - -namespace kOS.Suffixed.Part -{ - [kOS.Safe.Utilities.KOSNomenclature("ReactionWheel")] - public class ReactionWheelValue : PartValue - { - private readonly ModuleReactionWheel module; - - /// - /// Do not call! VesselTarget.ConstructPart uses this, would use `friend VesselTarget` if this was C++! - /// - internal ReactionWheelValue(SharedObjects shared, global::Part part, PartValue parent, DecouplerValue decoupler, ModuleReactionWheel module) - : base(shared, part, parent, decoupler) - { - this.module = module; - RegisterInitializer(RCSInitializeSuffixes); - } - private void RCSInitializeSuffixes() - { - AddSuffix("AUTHORITYLIMITER", new SetSuffix(() => module.authorityLimiter, value => module.authorityLimiter = Math.Max(Math.Min(value, 0), 100), "Sets the authority limiter for this reaction wheel.")); - AddSuffix("WHEELSTATE", new Suffix(() => module.wheelState.ToString().ToUpper(), "The status of the reaction wheel: ACTIVE, DISABLED or BROKEN.")); - AddSuffix("MAXTORQUE", new Suffix(() => new Vector(module.PitchTorque, module.YawTorque, module.RollTorque), "A vector representing max torque force over (pitch, yaw, roll).")); - AddSuffix("TORQUERESPONSESPEED", new Suffix(() => module.torqueResponseSpeed)); - } - - public static ListValue PartsToList(IEnumerable parts, SharedObjects sharedObj) - { - var toReturn = new ListValue(); - var vessel = VesselTarget.CreateOrGetExisting(sharedObj); - foreach (var part in parts) - { - foreach (var module in part.Modules) - { - if (module is ModuleReactionWheel) - { - toReturn.Add(vessel[part]); - // Only add each part once - break; - } - } - } - return toReturn; - } - } -} diff --git a/src/kOS/Suffixed/VesselTarget.Parts.cs b/src/kOS/Suffixed/VesselTarget.Parts.cs index a6692295d3..b1a6f76465 100644 --- a/src/kOS/Suffixed/VesselTarget.Parts.cs +++ b/src/kOS/Suffixed/VesselTarget.Parts.cs @@ -118,7 +118,6 @@ private void ConstructPart(global::Part part, PartValue parent, DecouplerValue d // gather all potential modules and then select from those valid. IEngineStatus engine = null; ModuleRCS rcs = null; - ModuleReactionWheel torqueWheel = null; PartValue separator = null; ModuleEnviroSensor sensor = null; @@ -132,10 +131,6 @@ private void ConstructPart(global::Part part, PartValue parent, DecouplerValue d { rcs = module as ModuleRCS; } - else if (module is ModuleReactionWheel) - { - torqueWheel = module as ModuleReactionWheel; - } else if (module is IStageSeparator) { var dock = module as ModuleDockingNode; @@ -182,8 +177,6 @@ private void ConstructPart(global::Part part, PartValue parent, DecouplerValue d self = new EngineValue(Shared, part, parent, decoupler); else if (rcs != null) self = new RCSValue(Shared, part, parent, decoupler, rcs); - else if (torqueWheel != null) - self = new ReactionWheelValue(Shared, part, parent, decoupler, torqueWheel); else if (separator != null) self = separator; else if (sensor != null) diff --git a/src/kOS/Suffixed/VesselTarget.cs b/src/kOS/Suffixed/VesselTarget.cs index f39f1980af..645f24751e 100644 --- a/src/kOS/Suffixed/VesselTarget.cs +++ b/src/kOS/Suffixed/VesselTarget.cs @@ -9,7 +9,9 @@ using kOS.Safe.Utilities; using kOS.Suffixed.Part; using kOS.Utilities; +using kOS.Control; using System; +using System.Linq; using System.Collections.Generic; using UnityEngine; @@ -259,6 +261,7 @@ private void InitializeSuffixes() AddSuffix("DOCKINGPORTS", new NoArgsSuffix>(() => DockingPorts)); AddSuffix(new string[] { "DECOUPLERS", "SEPARATORS" }, new NoArgsSuffix>(() => Decouplers)); AddSuffix("ELEMENTS", new NoArgsSuffix(() => Vessel.PartList("elements", Shared))); + AddSuffix("ATTITUDECONTROLLERS", new NoArgsSuffix(GetAttitudeControllers)); AddSuffix("CONTROL", new Suffix(GetFlightControl)); AddSuffix("BEARING", new Suffix(() => VesselUtils.GetTargetBearing(CurrentVessel, Vessel))); @@ -306,6 +309,12 @@ private void InitializeSuffixes() AddSuffix("CREWCAPACITY", new NoArgsSuffix(GetCrewCapacity)); AddSuffix("CONNECTION", new NoArgsSuffix(() => new VesselConnection(Vessel, Shared))); AddSuffix("MESSAGES", new NoArgsSuffix(() => GetMessages())); + AddSuffix("MOMENTOFINERTIA", new Suffix(() => + { + Vector3 moi = SteeringManager.FindMoI(Vessel); + // pitch, yaw, roll + return new Vector(moi.x, moi.z, moi.y); + })); AddSuffix("STARTTRACKING", new NoArgsVoidSuffix(StartTracking)); AddSuffix("STOPTRACKING", new NoArgsVoidSuffix(StopTracking)); @@ -411,6 +420,13 @@ public ScalarValue GetMaxThrustAt(ScalarValue atmPressure) return VesselUtils.GetMaxThrust(Vessel, atmPressure); } + public ListValue GetAttitudeControllers() + { + IEnumerable controllers = new AttitudeController[]{ }; + controllers = Parts.Aggregate(controllers, (result, part) => result.Concat(part.GetAttitudeControllers())); + return new ListValue(controllers); + } + private void RetypeVessel(StringValue value) { Vessel.vesselType = value.ToString().ToEnum(); diff --git a/src/kOS/Utilities/VesselUtils.cs b/src/kOS/Utilities/VesselUtils.cs index 5f492222bb..6d28b7012d 100644 --- a/src/kOS/Utilities/VesselUtils.cs +++ b/src/kOS/Utilities/VesselUtils.cs @@ -86,10 +86,6 @@ public static ListValue PartList(this IShipconstruct vessel, string partType, Sh list = RCSValue.PartsToList(partList, sharedObj); break; - case "REACTIONWHEELS": - list = ReactionWheelValue.PartsToList(partList, sharedObj); - break; - case "SENSORS": list = SensorValue.PartsToList(partList, sharedObj); break; diff --git a/src/kOS/kOS.csproj b/src/kOS/kOS.csproj index a634897a0d..3b0c88f553 100644 --- a/src/kOS/kOS.csproj +++ b/src/kOS/kOS.csproj @@ -163,6 +163,8 @@ + + From 7835831ee9644372e952fbb65c7a44d162bfb1a7 Mon Sep 17 00:00:00 2001 From: Max Maton Date: Tue, 9 Mar 2021 19:50:43 +0100 Subject: [PATCH 05/11] Fix missing reference --- src/kOS/kOS.csproj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/kOS/kOS.csproj b/src/kOS/kOS.csproj index 3b0c88f553..a3270b1b87 100644 --- a/src/kOS/kOS.csproj +++ b/src/kOS/kOS.csproj @@ -174,7 +174,6 @@ - @@ -336,4 +335,4 @@ - \ No newline at end of file + From 8f72f101482e28797db80b811608a21aae07ce9a Mon Sep 17 00:00:00 2001 From: Max Maton Date: Tue, 9 Mar 2021 20:01:34 +0100 Subject: [PATCH 06/11] Add AttitudeCorrectionResult docs --- .../vessels/attitudecorrectionresult.rst | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 doc/source/structures/vessels/attitudecorrectionresult.rst diff --git a/doc/source/structures/vessels/attitudecorrectionresult.rst b/doc/source/structures/vessels/attitudecorrectionresult.rst new file mode 100644 index 0000000000..341680f2bd --- /dev/null +++ b/doc/source/structures/vessels/attitudecorrectionresult.rst @@ -0,0 +1,23 @@ +.. _attitudecorrectionresult: + +Attitude Correction Result +====== + +When you perform a control action on a ship (yaw, pitch, roll, x, y, z, throttle) this always has two effects on the ship. Part of the impulse will be be imparted as rotation and part of it will be translation. + +.. structure:: AttitudeCorrectionResult + + .. list-table:: Members + :header-rows: 1 + :widths: 1 1 2 + + * - Suffix + - Type (units) + - Description + + * - :attr:`TORQUE` + - :struct:`Vector ` + - The torque vector (pitch, roll, yaw). + * - :attr:`TRANSLATION` + - :struct:`Vector ` + - The translation force (x, y, z) From f10592ed271cc5660373b93377bb8d49c112f4b9 Mon Sep 17 00:00:00 2001 From: Max Maton Date: Sat, 20 Mar 2021 23:52:03 +0100 Subject: [PATCH 07/11] Expose and document authority limiter attributes --- .../structures/vessels/attitudecontroller.rst | 24 +++++++++++++++++++ src/kOS/Suffixed/AttitudeController.cs | 3 +++ 2 files changed, 27 insertions(+) diff --git a/doc/source/structures/vessels/attitudecontroller.rst b/doc/source/structures/vessels/attitudecontroller.rst index 2e1dc4f06a..94845b0bcf 100644 --- a/doc/source/structures/vessels/attitudecontroller.rst +++ b/doc/source/structures/vessels/attitudecontroller.rst @@ -48,6 +48,12 @@ A ship usually has various attitude controllers like control surfaces, engines, * - :attr:`CUSTOMTHROTTLE` - :ref:`scalar ` (%) - The value the custom throttle. + * - :attr:`ROTATIONAUTHRORITYLIMITER` + - :ref:`scalar ` (%) + - The authority limit for rotation. + * - :attr:`TRANSLATIONAUTHRORITYLIMITER` + - :ref:`scalar ` (%) + - The authority limit for translation. * - :attr:`CONTROLLERTYPE` - :ref:`string ` - The type of the controller. @@ -164,6 +170,24 @@ A ship usually has various attitude controllers like control surfaces, engines, Sets the custom throttle for this controller. +.. _attitudecontroller_ROTATIONAUTHORITYLIMITER: + +.. attribute:: AttitudeController:ROTATIONAUTHORITYLIMITER + + :access: Get/Set + :type: :ref:`scalar ` (%) + + Sets the authority limiter used during rotation. + +.. _attitudecontroller_TRANSLATIONAUTHORITYLIMITER: + +.. attribute:: AttitudeController:TRANSLATIONAUTHORITYLIMITER + + :access: Get/Set + :type: :ref:`scalar ` (%) + + Sets the authority limiter used during translation. + .. _attitudecontroller_CONTROLLERTYPE: .. attribute:: AttitudeController:CONTROLLERTYPE diff --git a/src/kOS/Suffixed/AttitudeController.cs b/src/kOS/Suffixed/AttitudeController.cs index 5731f9c64e..6e65741ad2 100644 --- a/src/kOS/Suffixed/AttitudeController.cs +++ b/src/kOS/Suffixed/AttitudeController.cs @@ -45,6 +45,9 @@ public AttitudeController(PartValue part, PartModuleFields module, ITorqueProvid AddSuffix("POSITIVEROTATION", new Suffix(() => positiveRotation, "What is the torque applied when giving a positive input on pitch, roll and yaw.")); AddSuffix("NEGATIVEROTATION", new Suffix(() => negativeRotation, "What is the torque applied when giving a negative input on pitch, roll and yaw.")); + AddSuffix("ROTATIONAUTHRORITYLIMITER", new SetSuffix(() => rotationAuthorityLimiter, (v) => { rotationAuthorityLimiter = v; }, "The authority limit for rotating.")); + AddSuffix("TRANSLATIONAUTHRORITYLIMITER", new SetSuffix(() => translationAuthorityLimiter, (v) => { translationAuthorityLimiter = v; }, "The authority limit for translating.")); + AddSuffix("RESPONSEFOR", new ThreeArgsSuffix((rotation, translation, thrust) => { return GetResponseFor((float)rotation.X, (float)rotation.Y, (float)rotation.Z, (float)translation.X, (float)translation.Y, (float)translation.Z, thrust); From e8fde62462e02746619a479a0cef0bf03d4019a4 Mon Sep 17 00:00:00 2001 From: Max Maton Date: Sun, 11 Apr 2021 20:40:09 +0200 Subject: [PATCH 08/11] Use natural names instead of x,y,z --- doc/source/structures/vessels/attitudecontroller.rst | 8 ++++---- .../structures/vessels/attitudecorrectionresult.rst | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/source/structures/vessels/attitudecontroller.rst b/doc/source/structures/vessels/attitudecontroller.rst index 94845b0bcf..f65bda7073 100644 --- a/doc/source/structures/vessels/attitudecontroller.rst +++ b/doc/source/structures/vessels/attitudecontroller.rst @@ -132,7 +132,7 @@ A ship usually has various attitude controllers like control surfaces, engines, :access: Get/Set :type: :ref:`boolean ` - Determines whether this controller is allowed to respond to translation X input. + Determines whether this controller is allowed to respond to translation fore input. .. _attitudecontroller_ALLOWY: @@ -141,7 +141,7 @@ A ship usually has various attitude controllers like control surfaces, engines, :access: Get/Set :type: :ref:`boolean ` - Determines whether this controller is allowed to respond to translation Y input. + Determines whether this controller is allowed to respond to translation top input. .. _attitudecontroller_ALLOWZ: @@ -150,7 +150,7 @@ A ship usually has various attitude controllers like control surfaces, engines, :access: Get/Set :type: :ref:`boolean ` - Determines whether this controller is allowed to respond to translation Z input. + Determines whether this controller is allowed to respond to translation star input. .. _attitudecontroller_HASCUSTOMTHROTTLE: @@ -238,7 +238,7 @@ A ship usually has various attitude controllers like control surfaces, engines, .. method:: AttitudeController:RESPONSEFOR(pitchYawRollInput, translateXYZInput, throttle) :parameter pitchYawRollInput: A vector describing user pitch, yaw, roll input between -1 and 1. - :parameter translateXYZInput: A vector describing user x, y, z translation input between -1 and 1. + :parameter translateXYZInput: A vector describing user fore, top, star translation input between -1 and 1. :parameter throttle: A scalar representing the custom throttle value in percent. :type: :struct:`AttitudeCorrectionResult ` diff --git a/doc/source/structures/vessels/attitudecorrectionresult.rst b/doc/source/structures/vessels/attitudecorrectionresult.rst index 341680f2bd..c32d9fd2da 100644 --- a/doc/source/structures/vessels/attitudecorrectionresult.rst +++ b/doc/source/structures/vessels/attitudecorrectionresult.rst @@ -3,7 +3,7 @@ Attitude Correction Result ====== -When you perform a control action on a ship (yaw, pitch, roll, x, y, z, throttle) this always has two effects on the ship. Part of the impulse will be be imparted as rotation and part of it will be translation. +When you perform a control action on a ship (yaw, pitch, roll, fore, top, star, throttle) this always has two effects on the ship. Part of the impulse will be be imparted as rotation and part of it will be translation. .. structure:: AttitudeCorrectionResult @@ -20,4 +20,4 @@ When you perform a control action on a ship (yaw, pitch, roll, x, y, z, throttle - The torque vector (pitch, roll, yaw). * - :attr:`TRANSLATION` - :struct:`Vector ` - - The translation force (x, y, z) + - The translation force (fore, top, star) From 78c98053a40c9b28194c4e5004df9831fb8430da Mon Sep 17 00:00:00 2001 From: Dunbaratu Date: Mon, 12 Jul 2021 01:03:15 -0500 Subject: [PATCH 09/11] Fix typo in suffix name ("authrority"->"authority") --- doc/source/structures/vessels/attitudecontroller.rst | 4 ++-- src/kOS/Suffixed/AttitudeController.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/source/structures/vessels/attitudecontroller.rst b/doc/source/structures/vessels/attitudecontroller.rst index f65bda7073..481d4eee1a 100644 --- a/doc/source/structures/vessels/attitudecontroller.rst +++ b/doc/source/structures/vessels/attitudecontroller.rst @@ -48,10 +48,10 @@ A ship usually has various attitude controllers like control surfaces, engines, * - :attr:`CUSTOMTHROTTLE` - :ref:`scalar ` (%) - The value the custom throttle. - * - :attr:`ROTATIONAUTHRORITYLIMITER` + * - :attr:`ROTATIONAUTHORITYLIMITER` - :ref:`scalar ` (%) - The authority limit for rotation. - * - :attr:`TRANSLATIONAUTHRORITYLIMITER` + * - :attr:`TRANSLATIONAUTHORITYLIMITER` - :ref:`scalar ` (%) - The authority limit for translation. * - :attr:`CONTROLLERTYPE` diff --git a/src/kOS/Suffixed/AttitudeController.cs b/src/kOS/Suffixed/AttitudeController.cs index 6e65741ad2..d3e1b8c077 100644 --- a/src/kOS/Suffixed/AttitudeController.cs +++ b/src/kOS/Suffixed/AttitudeController.cs @@ -45,8 +45,8 @@ public AttitudeController(PartValue part, PartModuleFields module, ITorqueProvid AddSuffix("POSITIVEROTATION", new Suffix(() => positiveRotation, "What is the torque applied when giving a positive input on pitch, roll and yaw.")); AddSuffix("NEGATIVEROTATION", new Suffix(() => negativeRotation, "What is the torque applied when giving a negative input on pitch, roll and yaw.")); - AddSuffix("ROTATIONAUTHRORITYLIMITER", new SetSuffix(() => rotationAuthorityLimiter, (v) => { rotationAuthorityLimiter = v; }, "The authority limit for rotating.")); - AddSuffix("TRANSLATIONAUTHRORITYLIMITER", new SetSuffix(() => translationAuthorityLimiter, (v) => { translationAuthorityLimiter = v; }, "The authority limit for translating.")); + AddSuffix("ROTATIONAUTHORITYLIMITER", new SetSuffix(() => rotationAuthorityLimiter, (v) => { rotationAuthorityLimiter = v; }, "The authority limit for rotating.")); + AddSuffix("TRANSLATIONAUTHORITYLIMITER", new SetSuffix(() => translationAuthorityLimiter, (v) => { translationAuthorityLimiter = v; }, "The authority limit for translating.")); AddSuffix("RESPONSEFOR", new ThreeArgsSuffix((rotation, translation, thrust) => { From cbc5c874fd73b7d692a87a98927c6a5a60631a3e Mon Sep 17 00:00:00 2001 From: Dunbaratu Date: Mon, 12 Jul 2021 01:27:53 -0500 Subject: [PATCH 10/11] Renamed some variables as follows to fit with the rest of kOS: 1 - Elsewhere in kOS, the project uses the standard that Properties are capitalized just like Methods are. (Only local vars and fields that are passive (no getter/setter) are lowercased.) 2 - I renamed ``vesselTransform`` and ``vesselRotation`` inside SteeringManger.FindMOI() to ``vesTransform`` and ``vesRotation``. This is just a matter of preference that I'd like to make the names explicitly tell the reader these differ from the instance members of the same name. (i.e. not having to depend on noticing the method is static to realize what's going on.) I also started to write up more explicit documentation, where I would test each suffix and then write a more detailed description of what it does, but I didn't get past the first paragraph. I stopped when I started noticing how many of these suffixes are just hardcoded stubs for now that aren't actually implemented. I didn't realize most of the suffixes are just placeholders until I started trying to document them. (AllowPitch, AllowRoll, AllowYaw, AllowX, AllowY, AllowZ, RotationAuthorityLimiter, TranslationAuthorityLimiter, HasCustomThrottle, CustomThrottle, ControllerType, Status, ResponseTime.) --- .../structures/vessels/attitudecontroller.rst | 14 +- src/kOS/Control/SteeringManager.cs | 10 +- src/kOS/Suffixed/AttitudeController.cs | 172 +++++++++--------- 3 files changed, 101 insertions(+), 95 deletions(-) diff --git a/doc/source/structures/vessels/attitudecontroller.rst b/doc/source/structures/vessels/attitudecontroller.rst index 481d4eee1a..718cde758f 100644 --- a/doc/source/structures/vessels/attitudecontroller.rst +++ b/doc/source/structures/vessels/attitudecontroller.rst @@ -1,12 +1,18 @@ .. _attitudecontroller: Attitude Controller -====== +=================== -A ship usually has various attitude controllers like control surfaces, engines, RCS thrusters, rotors and drain valves. These controllers can be configured as follows: +A ship usually has various parts that can rotate or translate the ship. +(These include areodynamic control surfaces, engines with gimbaled thrust, +RCS thrusters, reaction wheels, and drain valves.) +Information about these controllers can be read, and in some cases set, +using the ``AttitudeController`` structure defined on this page. - local controllers to ship:AttitudeControllers. - print(controllers[0]:ControllerType + " allows pitch: " + controllers[0]:allowPitch). +Example:: + + local controllers is ship:AttitudeControllers. + print(controllers[0]:part + " allows pitch? " + controllers[0]:allowPitch). .. structure:: AttitudeController diff --git a/src/kOS/Control/SteeringManager.cs b/src/kOS/Control/SteeringManager.cs index ddc70d0c10..3622c397d2 100644 --- a/src/kOS/Control/SteeringManager.cs +++ b/src/kOS/Control/SteeringManager.cs @@ -778,9 +778,9 @@ public void CorrectedGetPotentialTorque(ITorqueProvider tp, out Vector3 pos, out /// TODO: Check this again after each KSP stock release to see if it's been changed or not. public static Vector3 FindMoI(Vessel vessel) { - var vesselTransform = vessel.ReferenceTransform; - var vesselRotation = vesselTransform.rotation * Quaternion.Euler(-90, 0, 0); - var centerOfMass = vessel.CoMD; + var vesTransform = vessel.ReferenceTransform; + var vesRotation = vesTransform.rotation * Quaternion.Euler(-90, 0, 0); + var vesCenterOfMass = vessel.CoMD; var tensor = Matrix4x4.zero; Matrix4x4 partTensor = Matrix4x4.identity; @@ -792,7 +792,7 @@ public static Vector3 FindMoI(Vessel vessel) { KSPUtil.ToDiagonalMatrix2(part.rb.inertiaTensor, ref partTensor); - Quaternion rot = Quaternion.Inverse(vesselRotation) * part.transform.rotation * part.rb.inertiaTensorRotation; + Quaternion rot = Quaternion.Inverse(vesRotation) * part.transform.rotation * part.rb.inertiaTensorRotation; Quaternion inv = Quaternion.Inverse(rot); Matrix4x4 rotMatrix = Matrix4x4.TRS(Vector3.zero, rot, Vector3.one); @@ -801,7 +801,7 @@ public static Vector3 FindMoI(Vessel vessel) // add the part inertiaTensor to the ship inertiaTensor KSPUtil.Add(ref tensor, rotMatrix * partTensor * invMatrix); - Vector3 position = vesselTransform.InverseTransformDirection(part.rb.position - centerOfMass); + Vector3 position = vesTransform.InverseTransformDirection(part.rb.position - vesCenterOfMass); // add the part mass to the ship inertiaTensor KSPUtil.ToDiagonalMatrix2(part.rb.mass * position.sqrMagnitude, ref inertiaMatrix); diff --git a/src/kOS/Suffixed/AttitudeController.cs b/src/kOS/Suffixed/AttitudeController.cs index d3e1b8c077..8072cb756b 100644 --- a/src/kOS/Suffixed/AttitudeController.cs +++ b/src/kOS/Suffixed/AttitudeController.cs @@ -18,8 +18,8 @@ public class AttitudeController : Structure { public AttitudeController(PartValue part, PartModuleFields module, ITorqueProvider provider) { - torqueProvider = provider; - this.part = part; + TorqueProvider = provider; + this.Part = part; AddSuffix("PART", new Suffix(() => part, "The part this AttitudeController belongs to.")); AddSuffix("MODULE", new Suffix(() => @@ -28,25 +28,25 @@ public AttitudeController(PartValue part, PartModuleFields module, ITorqueProvid return new BooleanValue(false); return module; }, "The module this AttitudeController belongs to.")); - AddSuffix("ALLOWPITCH", new SetSuffix(() => allowPitch, (v) => { allowPitch = v; }, "Should this attitude controller respond to pitch input?")); - AddSuffix("ALLOWROLL", new SetSuffix(() => allowRoll, (v) => { allowRoll = v; }, "Should this attitude controller respond to roll input?")); - AddSuffix("ALLOWYAW", new SetSuffix(() => allowYaw, (v) => { allowYaw = v; }, "Should this attitude controller respond to yaw input?")); - AddSuffix("ALLOWX", new SetSuffix(() => allowX, (v) => { allowX = v; }, "Should this attitude controller respond to translation X input?")); - AddSuffix("ALLOWY", new SetSuffix(() => allowY, (v) => { allowY = v; }, "Should this attitude controller respond to translation Y input?")); - AddSuffix("ALLOWZ", new SetSuffix(() => allowZ, (v) => { allowZ = v; }, "Should this attitude controller respond to translation Z input?")); - - AddSuffix("HASCUSTOMTHROTTLE", new Suffix(() => hasCustomThrottle, "Does this attitude controller have an independent custom throttle?")); - AddSuffix("CUSTOMTHROTTLE", new SetSuffix(() => customThrottle, (v) => { customThrottle = v; }, "The value of the independent custom throttle.")); - AddSuffix("CONTROLLERTYPE", new Suffix(() => controllerType, "The type of controller.")); - AddSuffix("STATUS", new Suffix(() => status, "The status of the controller.")); + AddSuffix("ALLOWPITCH", new SetSuffix(() => AllowPitch, (v) => { AllowPitch = v; }, "Should this attitude controller respond to pitch input?")); + AddSuffix("ALLOWROLL", new SetSuffix(() => AllowRoll, (v) => { AllowRoll = v; }, "Should this attitude controller respond to roll input?")); + AddSuffix("ALLOWYAW", new SetSuffix(() => AllowYaw, (v) => { AllowYaw = v; }, "Should this attitude controller respond to yaw input?")); + AddSuffix("ALLOWX", new SetSuffix(() => AllowX, (v) => { AllowX = v; }, "Should this attitude controller respond to translation X input?")); + AddSuffix("ALLOWY", new SetSuffix(() => AllowY, (v) => { AllowY = v; }, "Should this attitude controller respond to translation Y input?")); + AddSuffix("ALLOWZ", new SetSuffix(() => AllowZ, (v) => { AllowZ = v; }, "Should this attitude controller respond to translation Z input?")); + + AddSuffix("HASCUSTOMTHROTTLE", new Suffix(() => HasCustomThrottle, "Does this attitude controller have an independent custom throttle?")); + AddSuffix("CUSTOMTHROTTLE", new SetSuffix(() => CustomThrottle, (v) => { CustomThrottle = v; }, "The value of the independent custom throttle.")); + AddSuffix("CONTROLLERTYPE", new Suffix(() => ControllerType, "The type of controller.")); + AddSuffix("STATUS", new Suffix(() => Status, "The status of the controller.")); - AddSuffix("RESPONSETIME", new Suffix(() => responseTime, "The response speed of the controller.")); + AddSuffix("RESPONSETIME", new Suffix(() => ResponseTime, "The response speed of the controller.")); AddSuffix("POSITIVEROTATION", new Suffix(() => positiveRotation, "What is the torque applied when giving a positive input on pitch, roll and yaw.")); AddSuffix("NEGATIVEROTATION", new Suffix(() => negativeRotation, "What is the torque applied when giving a negative input on pitch, roll and yaw.")); - AddSuffix("ROTATIONAUTHORITYLIMITER", new SetSuffix(() => rotationAuthorityLimiter, (v) => { rotationAuthorityLimiter = v; }, "The authority limit for rotating.")); - AddSuffix("TRANSLATIONAUTHORITYLIMITER", new SetSuffix(() => translationAuthorityLimiter, (v) => { translationAuthorityLimiter = v; }, "The authority limit for translating.")); + AddSuffix("ROTATIONAUTHORITYLIMITER", new SetSuffix(() => RotationAuthorityLimiter, (v) => { RotationAuthorityLimiter = v; }, "The authority limit for rotating.")); + AddSuffix("TRANSLATIONAUTHORITYLIMITER", new SetSuffix(() => TranslationAuthorityLimiter, (v) => { TranslationAuthorityLimiter = v; }, "The authority limit for translating.")); AddSuffix("RESPONSEFOR", new ThreeArgsSuffix((rotation, translation, thrust) => { @@ -75,33 +75,33 @@ public static AttitudeController FromModule(PartValue part, PartModuleFields mod return null; } - protected PartValue part { get; private set; } - protected ITorqueProvider torqueProvider { get; private set; } - public virtual bool allowPitch { get { return false; } set { } } - public virtual bool allowRoll { get { return false; } set { } } - public virtual bool allowYaw { get { return false; } set { } } - public virtual bool allowX { get { return false; } set { } } - public virtual bool allowY { get { return false; } set { } } - public virtual bool allowZ { get { return false; } set { } } + protected PartValue Part { get; private set; } + protected ITorqueProvider TorqueProvider { get; private set; } + public virtual bool AllowPitch { get { return false; } set { } } + public virtual bool AllowRoll { get { return false; } set { } } + public virtual bool AllowYaw { get { return false; } set { } } + public virtual bool AllowX { get { return false; } set { } } + public virtual bool AllowY { get { return false; } set { } } + public virtual bool AllowZ { get { return false; } set { } } - public virtual float rotationAuthorityLimiter { get { return 0; } set { } } - public virtual float translationAuthorityLimiter { get { return 0; } set { } } + public virtual float RotationAuthorityLimiter { get { return 0; } set { } } + public virtual float TranslationAuthorityLimiter { get { return 0; } set { } } - public virtual bool hasCustomThrottle { get { return false; } } - public virtual float customThrottle { get { return 0; } set { } } - public virtual string controllerType { get { return "UNKNOWN"; } } - public virtual string status { get { return "UNKNOWN"; } } - public virtual float responseTime { get { return 0; } set { } } + public virtual bool HasCustomThrottle { get { return false; } } + public virtual float CustomThrottle { get { return 0; } set { } } + public virtual string ControllerType { get { return "UNKNOWN"; } } + public virtual string Status { get { return "UNKNOWN"; } } + public virtual float ResponseTime { get { return 0; } set { } } public override string ToString() { - return "AttitudeController(" + controllerType + ")"; + return "AttitudeController(" + ControllerType + ")"; } protected void GetPotentialTorque(out Vector3 positive, out Vector3 negative) { - var controlParameter = kOSVesselModule.GetInstance(part.Shared.Vessel).GetFlightControlParameter("steering"); + var controlParameter = kOSVesselModule.GetInstance(Part.Shared.Vessel).GetFlightControlParameter("steering"); var steeringManager = controlParameter as SteeringManager; if (steeringManager == null) @@ -112,7 +112,7 @@ protected void GetPotentialTorque(out Vector3 positive, out Vector3 negative) } Vector3 pos = Vector3.zero; Vector3 neg = Vector3.zero; - steeringManager.CorrectedGetPotentialTorque(torqueProvider, out pos, out neg); + steeringManager.CorrectedGetPotentialTorque(TorqueProvider, out pos, out neg); positive.x = pos.x; positive.y = pos.z; positive.z = pos.y; @@ -128,7 +128,7 @@ public virtual AttitudeCorrectionResult positiveRotation Vector3 _ = Vector3.zero; GetPotentialTorque(out pos, out _); - AttitudeCorrectionResult estimate = GetResponseFor(1, 1, 1, 0, 0, 0, customThrottle); + AttitudeCorrectionResult estimate = GetResponseFor(1, 1, 1, 0, 0, 0, CustomThrottle); return new AttitudeCorrectionResult(new Vector(pos), estimate.translation); } } @@ -140,7 +140,7 @@ public virtual AttitudeCorrectionResult negativeRotation Vector3 neg = Vector3.zero; GetPotentialTorque(out _, out neg); - AttitudeCorrectionResult estimate = GetResponseFor(-1, -1, -1, 0, 0, 0, customThrottle); + AttitudeCorrectionResult estimate = GetResponseFor(-1, -1, -1, 0, 0, 0, CustomThrottle); return new AttitudeCorrectionResult(new Vector(neg), estimate.translation); } } @@ -149,9 +149,9 @@ public virtual AttitudeCorrectionResult GetResponseFor(float pitch, float yaw, f return new AttitudeCorrectionResult(Vector.Zero, Vector.Zero); } - protected Vector3 StarVector { get { return VesselUtils.GetFacing(part.Shared.Vessel).Rotation * Vector3.right; } } - protected Vector3 TopVector { get { return VesselUtils.GetFacing(part.Shared.Vessel).Rotation * Vector3.up; } } - protected Vector3 ForeVector { get { return VesselUtils.GetFacing(part.Shared.Vessel).Rotation * Vector3.forward; } } + protected Vector3 StarVector { get { return VesselUtils.GetFacing(Part.Shared.Vessel).Rotation * Vector3.right; } } + protected Vector3 TopVector { get { return VesselUtils.GetFacing(Part.Shared.Vessel).Rotation * Vector3.up; } } + protected Vector3 ForeVector { get { return VesselUtils.GetFacing(Part.Shared.Vessel).Rotation * Vector3.forward; } } protected Vector3 PitchVector(Vector3 lever) { Vector3 projectedLever = Vector3.ProjectOnPlane(lever, StarVector); @@ -175,7 +175,7 @@ protected Vector3 RollVector(Vector3 lever) } protected Vector3 PositionToLever(Vector3 position) { - Vector3 shipCenterOfMass = part.Shared.Vessel.CoMD; + Vector3 shipCenterOfMass = Part.Shared.Vessel.CoMD; return position - shipCenterOfMass; } public static Direction GetTransformFacing(Transform transform) @@ -227,13 +227,13 @@ public AttitudeControllerReactionWheel(PartValue part, PartModuleFields module, protected ModuleReactionWheel wheel { get; private set; } - public override string controllerType { get { return "REACTIONWHEEL"; } } - public override string status { get { return wheel.State.ToString().ToUpper(); } } - public override float rotationAuthorityLimiter { get { return wheel.authorityLimiter; } set { wheel.authorityLimiter = Math.Max(Math.Min(value, 100), 0); } } - public override bool allowPitch { get { return true; } set { } } - public override bool allowRoll { get { return true; } set { } } - public override bool allowYaw { get { return true; } set { } } - public override float responseTime { get { return wheel.torqueResponseSpeed; } set { } } + public override string ControllerType { get { return "REACTIONWHEEL"; } } + public override string Status { get { return wheel.State.ToString().ToUpper(); } } + public override float RotationAuthorityLimiter { get { return wheel.authorityLimiter; } set { wheel.authorityLimiter = Math.Max(Math.Min(value, 100), 0); } } + public override bool AllowPitch { get { return true; } set { } } + public override bool AllowRoll { get { return true; } set { } } + public override bool AllowYaw { get { return true; } set { } } + public override float ResponseTime { get { return wheel.torqueResponseSpeed; } set { } } public override AttitudeCorrectionResult GetResponseFor(float pitch, float yaw, float roll, float x, float y, float z, float throttle) { Vector3 pos = Vector3.zero; @@ -265,11 +265,11 @@ public AttitudeControllerControlSurface(PartValue part, PartModuleFields module, protected ModuleControlSurface surface { get; private set; } - public override string controllerType { get { return "CONTROLSURFACE"; } } - public override bool allowPitch { get { return !surface.ignorePitch; } set { surface.ignorePitch = !value; } } - public override bool allowRoll { get { return !surface.ignoreRoll; } set { surface.ignoreRoll = !value; } } - public override bool allowYaw { get { return !surface.ignoreYaw; } set { surface.ignoreYaw = !value; } } - public override float rotationAuthorityLimiter { get { return surface.authorityLimiter; } set { surface.authorityLimiter = Math.Max(Math.Min(value, 100), 0); } } + public override string ControllerType { get { return "CONTROLSURFACE"; } } + public override bool AllowPitch { get { return !surface.ignorePitch; } set { surface.ignorePitch = !value; } } + public override bool AllowRoll { get { return !surface.ignoreRoll; } set { surface.ignoreRoll = !value; } } + public override bool AllowYaw { get { return !surface.ignoreYaw; } set { surface.ignoreYaw = !value; } } + public override float RotationAuthorityLimiter { get { return surface.authorityLimiter; } set { surface.authorityLimiter = Math.Max(Math.Min(value, 100), 0); } } public override AttitudeCorrectionResult GetResponseFor(float pitch, float yaw, float roll, float x, float y, float z, float throttle) { Vector3 pos = Vector3.zero; @@ -302,13 +302,13 @@ public AttitudeControllerRotor(PartValue part, PartModuleFields module, ModuleRo protected ModuleRoboticServoRotor rotor { get; private set; } - public override string controllerType { get { return "ROTOR"; } } + public override string ControllerType { get { return "ROTOR"; } } public override AttitudeCorrectionResult positiveRotation { get { return new AttitudeCorrectionResult(Vector.Zero, Vector.Zero); } } public override AttitudeCorrectionResult negativeRotation { get { return new AttitudeCorrectionResult(Vector.Zero, Vector.Zero); } } - public override bool hasCustomThrottle { get { return true; } } - public override float customThrottle { get { return rotor.maxTorque; } set { rotor.maxTorque = Math.Max(Math.Min(value, 100), 0); } } - public override string status { get { return rotor.motorState.ToUpper(); } } - public override float responseTime { get { return rotor.rotorSpoolTime; } set { } } + public override bool HasCustomThrottle { get { return true; } } + public override float CustomThrottle { get { return rotor.maxTorque; } set { rotor.maxTorque = Math.Max(Math.Min(value, 100), 0); } } + public override string Status { get { return rotor.motorState.ToUpper(); } } + public override float ResponseTime { get { return rotor.rotorSpoolTime; } set { } } public override AttitudeCorrectionResult GetResponseFor(float pitch, float yaw, float roll, float x, float y, float z, float throttle) { @@ -316,7 +316,7 @@ public override AttitudeCorrectionResult GetResponseFor(float pitch, float yaw, return new AttitudeCorrectionResult(Vector.Zero, Vector.Zero); // Likely makes too many assumptions about the axis direction but the rotation axis does not seem to be public. Quaternion rotator = rotor.servoTransformRotation; - Vector3d facing = part.GetFacing().Vector; + Vector3d facing = Part.GetFacing().Vector; Vector3 axis = new Vector3((float)facing.x, (float)facing.y, (float)facing.z); float pitchAlignment = Vector3.Dot(axis, StarVector); float yawAlignment = Vector3.Dot(axis, TopVector); @@ -384,7 +384,7 @@ public AttitudeControllerEngine(PartValue part, PartModuleFields module) protected List gimbals { get; private set; } protected Dictionary gimbalLinking { get; private set; } - public override bool allowPitch + public override bool AllowPitch { get { @@ -399,7 +399,7 @@ public override bool allowPitch gimbal.enablePitch = value; } } - public override bool allowRoll + public override bool AllowRoll { get { @@ -414,7 +414,7 @@ public override bool allowRoll gimbal.enableRoll = value; } } - public override bool allowYaw + public override bool AllowYaw { get { @@ -429,11 +429,11 @@ public override bool allowYaw gimbal.enableYaw = value; } } - public override AttitudeCorrectionResult positiveRotation { get { return GetResponseFor(1, 1, 1, 0, 0, 0, customThrottle); } } - public override AttitudeCorrectionResult negativeRotation { get { return GetResponseFor(-1, -1, -1, 0, 0, 0, customThrottle); } } - public override string controllerType { get { return "ENGINE"; } } + public override AttitudeCorrectionResult positiveRotation { get { return GetResponseFor(1, 1, 1, 0, 0, 0, CustomThrottle); } } + public override AttitudeCorrectionResult negativeRotation { get { return GetResponseFor(-1, -1, -1, 0, 0, 0, CustomThrottle); } } + public override string ControllerType { get { return "ENGINE"; } } - public override bool hasCustomThrottle + public override bool HasCustomThrottle { get { @@ -444,7 +444,7 @@ public override bool hasCustomThrottle return false; } } - public override float customThrottle + public override float CustomThrottle { get { @@ -460,7 +460,7 @@ public override float customThrottle engine.independentThrottlePercentage = Math.Max(Math.Min(value, 100), 0); } } - public override float responseTime + public override float ResponseTime { get { @@ -479,11 +479,11 @@ public override AttitudeCorrectionResult GetResponseFor(float pitch, float yaw, var engines = gimbalLinking.Keys; if (engines.Count == 0) return new AttitudeCorrectionResult(new Vector(0, 0, 0), new Vector(0, 0, 0)); - Vector3d facing3d = part.GetFacing().Vector; + Vector3d facing3d = Part.GetFacing().Vector; Vector3 facing = new Vector3((float)facing3d.x, (float)facing3d.y, (float)facing3d.z); - float shipThrottle = (float)kOSVesselModule.GetInstance(part.Shared.Vessel).GetFlightControlParameter("throttle").GetValue(); + float shipThrottle = (float)kOSVesselModule.GetInstance(Part.Shared.Vessel).GetFlightControlParameter("throttle").GetValue(); var result = new AttitudeCorrectionResult(new Vector(0, 0, 0), new Vector(0, 0, 0)); @@ -560,7 +560,7 @@ public AttitudeControllerDrainValve(PartValue part, PartModuleFields module, Mod protected ModuleResourceDrain drain { get; private set; } - public override string controllerType { get { return "DRAIN"; } } + public override string ControllerType { get { return "DRAIN"; } } public override AttitudeCorrectionResult positiveRotation { get { return new AttitudeCorrectionResult(Vector.Zero, Vector.Zero); } } public override AttitudeCorrectionResult negativeRotation { get { return new AttitudeCorrectionResult(Vector.Zero, Vector.Zero); } } public override AttitudeCorrectionResult GetResponseFor(float pitch, float yaw, float roll, float x, float y, float z, float throttle) @@ -569,20 +569,20 @@ public override AttitudeCorrectionResult GetResponseFor(float pitch, float yaw, // Assumes all resources are drained (not always true) float ISP = 5.0f; // All resources in ResourcesGeneric.cfg have a drain ISP of 5. - var parent = part.Parent; + var parent = Part.Parent; float weightDelta = parent.Part.GetWetMass() - parent.Part.GetDryMass(); float fuelFlow = weightDelta / 5; // Max throttle (20%) drains tank in 5 seconds float throttleFraction = Math.Min(Math.Max(throttle, 0), 20) / 20; float thrust = fuelFlow * ISP * 9.80665f * throttleFraction; - Vector3d facing = part.GetFacing().Vector; - Vector3 lever = PositionToLever(part.Part.transform.position); + Vector3d facing = Part.GetFacing().Vector; + Vector3 lever = PositionToLever(Part.Part.transform.position); return SplitComponents(lever, facing * thrust); } - public override bool hasCustomThrottle { get { return true; } } - public override float customThrottle + public override bool HasCustomThrottle { get { return true; } } + public override float CustomThrottle { get { return drain.drainRate; } set { drain.drainRate = Math.Min(Math.Max(value, 0), 20); } @@ -600,17 +600,17 @@ public AttitudeControllerRCS(PartValue part, PartModuleFields module, ModuleRCS protected ModuleRCS rcs { get; private set; } - public override string controllerType { get { return "RCS"; } } - public override bool allowPitch { get { return rcs.enablePitch; } set { rcs.enablePitch = value; } } - public override bool allowRoll { get { return rcs.enableRoll; } set { rcs.enableRoll = value; } } - public override bool allowYaw { get { return rcs.enableYaw; } set { rcs.enableYaw = value; } } - public override bool allowX { get { return rcs.enableX; } set { rcs.enableX = value; } } - public override bool allowY { get { return rcs.enableY; } set { rcs.enableY = value; } } - public override bool allowZ { get { return rcs.enableZ; } set { rcs.enableZ = value; } } + public override string ControllerType { get { return "RCS"; } } + public override bool AllowPitch { get { return rcs.enablePitch; } set { rcs.enablePitch = value; } } + public override bool AllowRoll { get { return rcs.enableRoll; } set { rcs.enableRoll = value; } } + public override bool AllowYaw { get { return rcs.enableYaw; } set { rcs.enableYaw = value; } } + public override bool AllowX { get { return rcs.enableX; } set { rcs.enableX = value; } } + public override bool AllowY { get { return rcs.enableY; } set { rcs.enableY = value; } } + public override bool AllowZ { get { return rcs.enableZ; } set { rcs.enableZ = value; } } public override AttitudeCorrectionResult GetResponseFor(float pitch, float yaw, float roll, float x, float y, float z, float throttle) { var result = new AttitudeCorrectionResult(new Vector(0, 0, 0), new Vector(0, 0, 0)); - if (part.Part.ShieldedFromAirstream || !rcs.rcsEnabled || !rcs.isEnabled || + if (Part.Part.ShieldedFromAirstream || !rcs.rcsEnabled || !rcs.isEnabled || rcs.isJustForShow || rcs.flameout || !rcs.rcs_active) return result; @@ -623,7 +623,7 @@ public override AttitudeCorrectionResult GetResponseFor(float pitch, float yaw, foreach (var transform in rcs.thrusterTransforms) { - Vector3 rcsPosFromCoM = transform.position - part.Part.vessel.CurrentCoM; + Vector3 rcsPosFromCoM = transform.position - Part.Part.vessel.CurrentCoM; Vector3 rcsThrustDir = rcs.useZaxis ? -transform.forward : transform.up; float powerFactor = rcs.thrusterPower * rcs.thrustPercentage * 0.01f; // Normally you'd check for precision mode to nerf powerFactor here, @@ -631,8 +631,8 @@ public override AttitudeCorrectionResult GetResponseFor(float pitch, float yaw, Vector3 thrust = powerFactor * rcsThrustDir; Vector3 torque = Vector3d.Cross(rcsPosFromCoM, thrust); Vector3 translation = rcsPosFromCoM.normalized * (float)Vector3d.Dot(rcsPosFromCoM.normalized, thrust); - Vector3 transformedTorque = part.Part.vessel.ReferenceTransform.InverseTransformDirection(torque); - Vector3 transformedTranslation = part.Part.vessel.ReferenceTransform.InverseTransformDirection(translation); + Vector3 transformedTorque = Part.Part.vessel.ReferenceTransform.InverseTransformDirection(torque); + Vector3 transformedTranslation = Part.Part.vessel.ReferenceTransform.InverseTransformDirection(translation); Vector3 desiredTorque = new Vector3(maskedPitch, maskedRoll, maskedYaw); Vector3 desiredTranslation = new Vector3(maskedX, maskedY, maskedZ); From 0df387253c6bd54afa22ff17fdaf167891cccbcc Mon Sep 17 00:00:00 2001 From: Max Maton Date: Wed, 21 Jul 2021 01:16:56 +0200 Subject: [PATCH 11/11] Make clearer that not all controllers can do everything --- .../structures/vessels/attitudecontroller.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/source/structures/vessels/attitudecontroller.rst b/doc/source/structures/vessels/attitudecontroller.rst index 718cde758f..e178eba203 100644 --- a/doc/source/structures/vessels/attitudecontroller.rst +++ b/doc/source/structures/vessels/attitudecontroller.rst @@ -111,7 +111,7 @@ Example:: :access: Get/Set :type: :ref:`boolean ` - Determines whether this controller is allowed to respond to pitch input. + Determines whether this controller is allowed to respond to pitch input. Not all controller types allow setting this, make sure to check the value after setting it! .. _attitudecontroller_ALLOWYAW: @@ -120,7 +120,7 @@ Example:: :access: Get/Set :type: :ref:`boolean ` - Determines whether this controller is allowed to respond to yaw input. + Determines whether this controller is allowed to respond to yaw input. Not all controller types allow setting this, make sure to check the value after setting it! .. _attitudecontroller_ALLOWROLL: @@ -129,7 +129,7 @@ Example:: :access: Get/Set :type: :ref:`boolean ` - Determines whether this controller is allowed to respond to roll input. + Determines whether this controller is allowed to respond to roll input. Not all controller types allow setting this, make sure to check the value after setting it! .. _attitudecontroller_ALLOWX: @@ -138,7 +138,7 @@ Example:: :access: Get/Set :type: :ref:`boolean ` - Determines whether this controller is allowed to respond to translation fore input. + Determines whether this controller is allowed to respond to translation fore input. Not all controller types allow setting this, make sure to check the value after setting it! .. _attitudecontroller_ALLOWY: @@ -147,7 +147,7 @@ Example:: :access: Get/Set :type: :ref:`boolean ` - Determines whether this controller is allowed to respond to translation top input. + Determines whether this controller is allowed to respond to translation top input. Not all controller types allow setting this, make sure to check the value after setting it! .. _attitudecontroller_ALLOWZ: @@ -156,7 +156,7 @@ Example:: :access: Get/Set :type: :ref:`boolean ` - Determines whether this controller is allowed to respond to translation star input. + Determines whether this controller is allowed to respond to translation star input. Not all controller types allow setting this, make sure to check the value after setting it! .. _attitudecontroller_HASCUSTOMTHROTTLE: @@ -174,7 +174,7 @@ Example:: :access: Get/Set :type: :ref:`scalar ` (%) - Sets the custom throttle for this controller. + Sets the custom throttle for this controller. Not all controller types allow setting this, make sure to check the value after setting it! .. _attitudecontroller_ROTATIONAUTHORITYLIMITER: @@ -183,7 +183,7 @@ Example:: :access: Get/Set :type: :ref:`scalar ` (%) - Sets the authority limiter used during rotation. + Sets the authority limiter used during rotation. Not all controller types allow setting this, make sure to check the value after setting it! .. _attitudecontroller_TRANSLATIONAUTHORITYLIMITER: @@ -192,7 +192,7 @@ Example:: :access: Get/Set :type: :ref:`scalar ` (%) - Sets the authority limiter used during translation. + Sets the authority limiter used during translation. Not all controller types allow setting this, make sure to check the value after setting it! .. _attitudecontroller_CONTROLLERTYPE: