Steven Mading

[1.7.3] kOS v1.1.9.0 : kOS Scriptable Autopilot System

Recommended Posts

11 hours ago, TBenz said:

1.


LOCK STEERING TO PROGRADE.

2. Stock Action Groups

Thanks for the help.

Now, i made a script to get into orbit... my rocket (RO sputnik 1) just after launch started gone to a state of spinning along the ground relative axis. Can someone figure out what's wrong in this script? I'm unable to do it even after an hour.

clearscreen.


from {local countdown is 10.} until countdown = 0 step {set countdown to countdown - 1.} do {print countdown + ".". wait 1.}
print "0, spooling engines...".

stage. lock throttle to 1.0. lock steering to heading(90,90).


wait 3. stage. print "liftoff!".

if ship:velocity:surface:mag < 100 {lock steering to heading(90,90).}
lock steering to heading(90,85).
wait 10.
lock steering to prograde.

wait 86. stage.

wait until ship:maxthrust = 0.

Share this post


Link to post
Share on other sites
44 minutes ago, KnedlikMCPE said:

if ship:velocity:surface:mag < 100 {lock steering to heading(90,90).}

That should probably be wait until ship:velocity:surface:mag >= 100.

At the moment you immediately try to pitch over after liftoff (it checks the if once then carries on).

Share this post


Link to post
Share on other sites
1 hour ago, Flibble said:

That should probably be wait until ship:velocity:surface:mag >= 100.

At the moment you immediately try to pitch over after liftoff (it checks the if once then carries on).

Thanks. That’s the first part fixed. Now, i got problem at “lock steering to prograde”, where it isn’t following the prograde marker but pitching to about 20-30 degrees surface axis.

Share this post


Link to post
Share on other sites

Prograde is probably orbital prograde. Try Ship:velocity:surface.

Share this post


Link to post
Share on other sites
43 minutes ago, Flibble said:

Prograde is probably orbital prograde. Try Ship:velocity:surface.

I’ve changed it and it’s following the prograde now. Altho, the ships wiggles left and right from the prograde (on navball). That’s final thing i need to repair.

Share this post


Link to post
Share on other sites
26 minutes ago, KnedlikMCPE said:

I’ve changed it and it’s following the prograde now. Altho, the ships wiggles left and right from the prograde (on navball). That’s final thing i need to repair.

How much wiggle? If could be the steering manager tuning, which is annoying to fix.

I have a rocket that won't settle its roll no matter what I do with steering.

Share this post


Link to post
Share on other sites
2 hours ago, Flibble said:

How much wiggle? If could be the steering manager tuning, which is annoying to fix.

I have a rocket that won't settle its roll no matter what I do with steering.

Once i get to my pc, i will make a video (with watermark if its ok) showing it. By eye, i could say up to 5 degrees on the heading axis. I don’t mean roll but yaw as this happens at pitch program after reaching prograde vector relative to the ground 90,85. No roll but yaw.

Share this post


Link to post
Share on other sites

Can someone answer the flying left and right from the prograde marker and how to fix it?

Share this post


Link to post
Share on other sites
2 hours ago, KnedlikMCPE said:

Hi again, thanks for the help! I edited the script but... it can’t understand command “wait 10.” Why?

What error do you get?

Share this post


Link to post
Share on other sites
22 minutes ago, Flibble said:

What error do you get?

That kOS cannot understand. Simply the one that you get for example when you forgot to add period at the end. Error indicator arrow is pointing for w in wait, which is usually when it’s that command.

Share this post


Link to post
Share on other sites
14 minutes ago, KnedlikMCPE said:

That kOS cannot understand. Simply the one that you get for example when you forgot to add period at the end. Error indicator arrow is pointing for w in wait, which is usually when it’s that command.

Check the lines above. It usually means you made a mistake before that and the parser had only just caught up.

Share this post


Link to post
Share on other sites
37 minutes ago, Flibble said:

Check the lines above. It usually means you made a mistake before that and the parser had only just caught up.

Can't find any mistake above. I copied the script:

I'm not sure about the roll manager tho.

---------------------------------------------------

clearscreen.

//Put into desired state.
sas off.
rcs off.
brakes off.
gear off.
lights off.

//Countdown loop.
from {local countdown is 10.} until countdown = 0 step {set countdown to countdown - 1.} do {print countdown + ".". wait 1.}
print "0, spooling engines...".

//Engine spool-up.
stage. lock throttle to 1.0. lock steering to heading(90,90).

//Wait while spooling and clamp release.
wait 3. stage. print "liftoff!".

//Detect ship speed of 100m/s
wait until ship:velocity:surface:mag >= 100.
print "Pitch program.".

//Pitch program for 116 seconds.
set steeringmanager:yawts to 5. set steeringmanager:rollts to 5.
lock steering to heading(90,85).
wait 10. 
lock steering to steer. set steer to heading(90,80).
wait 10.
set steer to heading(90,75).
wait 10.
set steer to heading(90,70). set steeringmanager:rollts to 10. set steeringmanager:yawts to 10.
wait 10.
set steer to heading(90,65).
wait 10.
set steer to heading(90,60). set steeringmanager:rollts to 15. set steeringmanager:yawts to 15
wait 10.
set steer to heading(90,55).
wait 10.
set steer to heading(90,50). set steeringmanager:rollts to 20. set steeringmanager:yawts to 20.
wait 10.
set steer to heading(90,45).
wait 10.
set steer to heading(90,40). set steeringmanager:rollts to 25. set steeringmanager:yawts to 25.
wait 10. 
set steer to heading(90,35).
wait 4.
set steering to ship:velocity:surface. set steeringmanager:rollts to 30.
set steeringmanager:yawts to 30.

//Booster seperation.
wait 4.
stage.

//Wait to end program only when fuel is on 0.
wait until ship:maxthrust = 0.

-end-

Share this post


Link to post
Share on other sites
2 minutes ago, KnedlikMCPE said:

set steeringmanager:yawts to 15

Missing a period.

Share this post


Link to post
Share on other sites
10 minutes ago, Flibble said:

Missing a period.

Oof mistake of a beginner. Thanks a lot!

Share this post


Link to post
Share on other sites
Posted (edited)

(This is a continuation of something that started in another thread over here:)

5 hours ago, TriggerAu said:

Whats the method that kOS etc use to try and read these values? eg. if we exposed the values as Properties, vs fields, vs KSPFields would it make a difference or usage in kOS, etc?

 

I'll try to explain it tersely, since I figure you don't want to go down the rabbit-hole of getting deep into the kOS code. 

Problem 1 - reading values that don't always update if the PAW isn't showing:

An example of a "read" value that seems to only be reliable if the PAW is open is this: On a ModuleRoboticServoPiston, the  KSPField "Current Extension", we get the BaseField from the gui name on the panel with this code (this code is generic for all PartModules, but so far has only exhibited this problem on the new servo PartModules):
 

        /// <summary>
        /// Return the field itself that goes with the name (the BaseField, not the value).
        /// </summary>
        /// <param name="cookedGuiName">The case-insensitive guiName (or name if guiname is empty) of the field.</param>
        /// <returns>a BaseField - a KSP type that can be used to get the value, or its GUI name or its reflection info.</returns>
        protected BaseField GetField(string cookedGuiName)
        {
            return partModule.Fields.Cast<BaseField>().
                FirstOrDefault(field => string.Equals(GetFieldName(field), cookedGuiName, StringComparison.CurrentCultureIgnoreCase));
        }

Then once that BaseField is found, then we retrieve its value with this:

field.GetValue(partModule)

This *almost* works all the time.  If the PAW is open, it works.  If the PAW *was* recently open it works *for a short while*, but eventually the value freezes and stops being updated if the PAW was closed maybe 30 seconds or so ago.  (Maybe the amount of time it takes for something inside SQUAD's code to get garbage collected?  I don't know, but if *feels* about that long.)

Problem 2 - Setting a KSPField that has a UIFloatRange control on it (sliders in the PAW that are tied to floats) that has range values that describe restrictions the UI seems to be ignoring or getting from somewhere else.

Example: There is a KSPField called "Target Extension", on the ModuleRoboticServoPiston.

The actual behaviour the game has when a player manually uses the mouse on that field in the PAW appears to be this:
The allowed range of values is [0.0 .. 2.4], without enforcing a stepIncrement (i.e. it allows the slider to land on any un-rounded value like 0.342010301 or whatever.  It may round the display using a string formatter, but the actual value isn't rounded.)

But the behaviour the game appears to be "advertising" with its UIFloatRange control attached to that KSPField is totally unlike that.  The UIFloatRange is claiming the following which just isn't true at all:
The allowed range of values is [0.0 to 100.0], with rounding to the nearest 1.0.  (UIFloatRange.minValue = 0f, UIFloatRange.maxValue =100f, UIFloatRange.stepIncrement = 1f).

The effect this is having on kOS is that because kOS has to enforce these UIFloatRange rules in all *other* PartModules to prevent scripts from "cheating" (see below), any script attempting to set a ModuleRoboticServoPiston's extension to something like 0.342 will end up setting it to 0 instead.  If it tries to set it to 0.51, it will become 1.0.  Because kOS is enforcing what UIFloatRange.stepIncrement is claiming, and in this case it's claiming Target Extension has to be rounded to the nearest 1.0, that means a kOS script can only set the piston to exactly 0 meters, or 1 meter, or 2 meters, with no other in-between values allowed.

I have the beginnings of a special derived class of the PartModule wrapper in kOS to try to discover when the PartModule is of type BaseServo and when it is, to suppress its normal enforcement of these ranges and instead look for the values from elsewhere (I've worked out what I think is the path to get from the field to its actual ranges in these cases (which are taken from one of the other fields set in the VAB) , but that's not an optimal solution as it requires treating BaseServo PartModules differently from all the rest.  Instead of a test that says "if type is BaseServo then look for the range value here instead", I'd prefer something that is class-agnostic about it, if there is a data field or property that tells me "this UIFloatRange's values should be taken from over Here instead".

(An Explanation of why kOS has to enforce the UIFloatRange min, max, and stepIncrement values for all the other PartModules):   Many of KSP's PartModules (both in stock parts and in modded parts) appear to have no protection against a mod like KSP setting KSPFields to values a user wouldn't be allowed to in the UI.  kOS has those range enforcements because otherwise a kerboscript could simply do something like set an engine's Thrust Limiter to 5000%, cheating the engine to be 50 times stronger than it should be, or set a Reaction Wheel's "Wheel Authority" to 400% and cheat the wheel to be 4 times stronger than it should be. kOS takes the strategy of not allowing a script to do anything a player isn't allowed to do, so we check what the PAW's controls are allowing and enforce that.  (Not just float ranges, but also not letting a script set a value when it's marked as hidden from the PAW right now, or press a KSPEvent button when the PartModule has decided that it shouldn be masked from view on the PAW right now (thus a script can't for example, Decouple an already decoupled docking port).)

One problem with what kOS is doing is that I know it's not proper form to try to make an API out of a user interface, but we do it because it's the only reliable way to ensure we aren't letting a kerboscript do something the player isn't allowed to, or that would confuse other PartModules' code.  (Just because a class made a KSPFIeld or a KSPEvent public doesn't mean they expected any random user to be able to alter the value at will - so we only alter the values when the UI would normally allow it - under the assumption that a PartModule would be written to expect the value to change unexpectedly if it has opened it up for players to mess with it via the PAW.  So far this technique has worked well as a generic way to work on all PartModules without having to write hundreds of special case kOS wrapper classes to access the hundreds of different kinds of PartModules.  But the servos in Breaking Ground seem to have broken some of our assumptions about how to detect what PartModules allow and don't allow.)

Edited by Steven Mading

Share this post


Link to post
Share on other sites
Posted (edited)

Topic: How to traverse the list of orbit patches in a maneuver, without just using a huge set of if-statements.

I did a few searches and couldn't find this topic, sorry if it is a duplicate.

I'm trying to write a kOS script (based on the "cheerskevin" youtube series) that will auto-launch from Kerbin to a hoverslam landing on Minmus.  This is his video series that uses a ternary search to find the time when the current trajectory will have the minimum distance to the Mun. I'm expanding the script so it can auto-land on Minmus instead.

Obviously, the Mun occasionally gets in the way, so the trajectory planner has to cope with the maneuver having multiple orbit patches. Sometimes these Mun encounters provide a nice gravity assist, so that's handy.

But my code has a problem.

Is there a way to artfully traverse the list of patches within a maneuver node, so that I can find the right patch on which to base the "proximity to Minmus" calculations?

I tried this sort of program flow (just pseudocode)

if maneuver:orbit:hasNextPatch { set orb to orbit:nextPatch }

...within a loop, trying to find a patch that applies to Minmus, or stop when it finds that hasNextPatch is false,. But the assignment of "set orb to ..." doesn't seem to work. I got an infinite loop with every patch belonging to

Kerbin.

Update: Aha. I think I found the problem - I was stupidly using the variable 'obt" for my local orbit, but "obt" is a reserved keyword for "orbit". That might have been monkeying up the works.

This seems to get the job done:

  // note: 'm' is the current maneuver node  
  set orb to m:orbit.
  // traverse the list of patches
  until not orb:hasnextpatch {
	print "Body:" + orb:body.
	print "ecc :" + orb:eccentricity.
	print "Per :" + orb:periapsis.
	set orb to orb:nextpatch.
  }
  
  print "Final patch: Body " + orb:body.

I'll leave this post here in case others find it useful.

The basic task is to traverse all the patches from a single maneuver that causes a trajectory that starts in the Kerbin SOI, flies into the Mun SOI, then back into the Kerbin SOI, then into the Minmus SOI. That's four patches, all triggered by one Maneuver. 

- Tom

 

Edited by FloppyRocket
Provided my own solution, added more info

Share this post


Link to post
Share on other sites
14 hours ago, Steven Mading said:

(This is a continuation of something that started in another thread over here:)

 

I'll try to explain it tersely, since I figure you don't want to go down the rabbit-hole of getting deep into the kOS code. 

Problem 1 - reading values that don't always update if the PAW isn't showing:

An example of a "read" value that seems to only be reliable if the PAW is open is this: On a ModuleRoboticServoPiston, the  KSPField "Current Extension", we get the BaseField from the gui name on the panel with this code (this code is generic for all PartModules, but so far has only exhibited this problem on the new servo PartModules):
 


        /// <summary>
        /// Return the field itself that goes with the name (the BaseField, not the value).
        /// </summary>
        /// <param name="cookedGuiName">The case-insensitive guiName (or name if guiname is empty) of the field.</param>
        /// <returns>a BaseField - a KSP type that can be used to get the value, or its GUI name or its reflection info.</returns>
        protected BaseField GetField(string cookedGuiName)
        {
            return partModule.Fields.Cast<BaseField>().
                FirstOrDefault(field => string.Equals(GetFieldName(field), cookedGuiName, StringComparison.CurrentCultureIgnoreCase));
        }

Then once that BaseField is found, then we retrieve its value with this:


field.GetValue(partModule)

This *almost* works all the time.  If the PAW is open, it works.  If the PAW *was* recently open it works *for a short while*, but eventually the value freezes and stops being updated if the PAW was closed maybe 30 seconds or so ago.  (Maybe the amount of time it takes for something inside SQUAD's code to get garbage collected?  I don't know, but if *feels* about that long.)

Problem 2 - Setting a KSPField that has a UIFloatRange control on it (sliders in the PAW that are tied to floats) that has range values that describe restrictions the UI seems to be ignoring or getting from somewhere else.

Example: There is a KSPField called "Target Extension", on the ModuleRoboticServoPiston.

The actual behaviour the game has when a player manually uses the mouse on that field in the PAW appears to be this:
The allowed range of values is [0.0 .. 2.4], without enforcing a stepIncrement (i.e. it allows the slider to land on any un-rounded value like 0.342010301 or whatever.  It may round the display using a string formatter, but the actual value isn't rounded.)

But the behaviour the game appears to be "advertising" with its UIFloatRange control attached to that KSPField is totally unlike that.  The UIFloatRange is claiming the following which just isn't true at all:
The allowed range of values is [0.0 to 100.0], with rounding to the nearest 1.0.  (UIFloatRange.minValue = 0f, UIFloatRange.maxValue =100f, UIFloatRange.stepIncrement = 1f).

The effect this is having on kOS is that because kOS has to enforce these UIFloatRange rules in all *other* PartModules to prevent scripts from "cheating" (see below), any script attempting to set a ModuleRoboticServoPiston's extension to something like 0.342 will end up setting it to 0 instead.  If it tries to set it to 0.51, it will become 1.0.  Because kOS is enforcing what UIFloatRange.stepIncrement is claiming, and in this case it's claiming Target Extension has to be rounded to the nearest 1.0, that means a kOS script can only set the piston to exactly 0 meters, or 1 meter, or 2 meters, with no other in-between values allowed.

I have the beginnings of a special derived class of the PartModule wrapper in kOS to try to discover when the PartModule is of type BaseServo and when it is, to suppress its normal enforcement of these ranges and instead look for the values from elsewhere (I've worked out what I think is the path to get from the field to its actual ranges in these cases (which are taken from one of the other fields set in the VAB) , but that's not an optimal solution as it requires treating BaseServo PartModules differently from all the rest.  Instead of a test that says "if type is BaseServo then look for the range value here instead", I'd prefer something that is class-agnostic about it, if there is a data field or property that tells me "this UIFloatRange's values should be taken from over Here instead".

(An Explanation of why kOS has to enforce the UIFloatRange min, max, and stepIncrement values for all the other PartModules):   Many of KSP's PartModules (both in stock parts and in modded parts) appear to have no protection against a mod like KSP setting KSPFields to values a user wouldn't be allowed to in the UI.  kOS has those range enforcements because otherwise a kerboscript could simply do something like set an engine's Thrust Limiter to 5000%, cheating the engine to be 50 times stronger than it should be, or set a Reaction Wheel's "Wheel Authority" to 400% and cheat the wheel to be 4 times stronger than it should be. kOS takes the strategy of not allowing a script to do anything a player isn't allowed to do, so we check what the PAW's controls are allowing and enforce that.  (Not just float ranges, but also not letting a script set a value when it's marked as hidden from the PAW right now, or press a KSPEvent button when the PartModule has decided that it shouldn be masked from view on the PAW right now (thus a script can't for example, Decouple an already decoupled docking port).)

One problem with what kOS is doing is that I know it's not proper form to try to make an API out of a user interface, but we do it because it's the only reliable way to ensure we aren't letting a kerboscript do something the player isn't allowed to, or that would confuse other PartModules' code.  (Just because a class made a KSPFIeld or a KSPEvent public doesn't mean they expected any random user to be able to alter the value at will - so we only alter the values when the UI would normally allow it - under the assumption that a PartModule would be written to expect the value to change unexpectedly if it has opened it up for players to mess with it via the PAW.  So far this technique has worked well as a generic way to work on all PartModules without having to write hundreds of special case kOS wrapper classes to access the hundreds of different kinds of PartModules.  But the servos in Breaking Ground seem to have broken some of our assumptions about how to detect what PartModules allow and don't allow.)

 

I'm pretty sure I understand both of these

Problem 1:

Is going to be because that is a string field for display and is only worked on when the PAW is open to cut down on the string formatting work, Garbage and processing. My suggestion will be that we expose a "value" that's always updated (if there is not already). Is it easier if these are fields or methods or irrelevant?

Problem 2: 

This one is more interesting, I can understand the use of the UI fields to help with the limits, but it appears that what you've got there is the attributes on the field definition and not the range of the UI Controls themselves. This would be especially common in the robotics parts as the player can change the limits in edit mode. The way we do this in the stock code is to grab the UI_FloatRange control and read its values. Its not a new problem for range controls im sure, but much more obvious with the way they share modules. So a simple method to demonstrate how you could read that sorta stuff is below
 

/// <summary>
/// Get the Min Value from a fields UI Control
/// </summary>
/// <param name="p">PartModule containing the field</param>
/// <param name="fieldName">the name of the field</param>
/// <param name="minValue">the min Value from the UI_FloatRange</param>
/// <returns>True if the field exists</returns>
private bool GetRangeMinValue(PartModule p, string fieldName, out float minValue)
{
    UI_FloatRange rangeControl;
    if (p.Fields.TryGetFieldUIControl(fieldName, out rangeControl))
    {
        minValue = rangeControl.minValue;
        return true;
    }

    minValue = 0;
    return false;
}

 

 

Share this post


Link to post
Share on other sites
Posted (edited)
6 hours ago, TriggerAu said:

 

I'm pretty sure I understand both of these

Problem 1:

Is going to be because that is a string field for display and is only worked on when the PAW is open to cut down on the string formatting work, Garbage and processing. My suggestion will be that we expose a "value" that's always updated (if there is not already). Is it easier if these are fields or methods or irrelevant?
 

Thank you for the reply.  Exposing the value as a float (not a string that we in turn have to decode back into a float again) would be ideal.  The reason we use the KSPField, KSPEvent, and KSPAction systems to expose PartModules to kOS scripts is that these are universally a system all PartModules use (so we don't have to write hundreds of different wrapper classes around the hundreds of different kinds of PartModules in the game).   And they let the players' scripts use names they can learn by just looking at the game UI.  Exposing a KSPfield value on the PAW that is readable as a float (not as a string) would be the easiest from our view, as it fits in with what we already do.  Also, if it's a KSPField, BUT it is marked as being hidden from the gui, then what will happen (unless we code a special exception case just for this one instance) is that kOS will still deny a script from seeing it, again under the design principle that any method or field that  isn't explicitly exposed to the player is one kOS assumes it wasn't given permission to show to the player by the author of the PartModule.

Here's an example of what is done with the mod LaserDist - a mod that was designed specifically with kOS in mind.  It exposes a "distance" KSPField on the PAW for kOS to be able to read with its generic PartModule KSPField scraping code.  The field *does* have some string formatting to the display, *but* that formatting is done by the guiFormat attribute value instead of the field being an *actual* string.  i.e. it is defined like this:
 

        /// <summary>Distance the laser is showing to the first collision:</summary>
        [KSPField(isPersistant=true, guiName = "Distance", guiActive = true, guiActiveEditor = true, guiUnits = "m", guiFormat = "N2")]
        public float Distance = 0.0f;

Doing something like that (where the field is really a float, but just displays a string via formatting) means that while the player sees the string with the "m" attached to the end, rounded to the nearest 1/100th, the actual float value of the field is what a kOS script sees.  I have no idea if this is do-able without hassle in the servo modules, or if the string assumption is buried "too deep" in how it works.

Quote

Problem 2: 

This one is more interesting, I can understand the use of the UI fields to help with the limits, but it appears that what you've got there is the attributes on the field definition and not the range of the UI Controls themselves. This would be especially common in the robotics parts as the player can change the limits in edit mode. The way we do this in the stock code is to grab the UI_FloatRange control and read its values. Its not a new problem for range controls im sure, but much more obvious with the way they share modules. So a simple method to demonstrate how you could read that sorta stuff is below
 


/// <summary>
/// Get the Min Value from a fields UI Control
/// </summary>
/// <param name="p">PartModule containing the field</param>
/// <param name="fieldName">the name of the field</param>
/// <param name="minValue">the min Value from the UI_FloatRange</param>
/// <returns>True if the field exists</returns>
private bool GetRangeMinValue(PartModule p, string fieldName, out float minValue)
{
    UI_FloatRange rangeControl;
    if (p.Fields.TryGetFieldUIControl(fieldName, out rangeControl))
    {
        minValue = rangeControl.minValue;
        return true;
    }

    minValue = 0;
    return false;
}

Well, we do use UI_FloatRange already, but not with that specific code.  I didn't know about the call TryGetFieldUIControl - instead we do it in two steps:  Step 1 - Get the field. Step 2 - After getting the field, then use the UI_FloatControl that is attached to that field.  It appears that the logic to bypass the range limits of the current field and instead take the range limits from a different field are buried inside this TryGetFieldUIControl call, if I understand what you're saying.  If that is the case then we should be able to fix the problem by using that technique instead.  It will require some refactor of what we're doing,  but it if it works in a more universal way then it's worth it.  Thank you for the help, and I'll be able to try it tomorrow or the day after.

Edited by Steven Mading

Share this post


Link to post
Share on other sites
2 hours ago, Steven Mading said:

Thank you for the reply.  Exposing the value as a float (not a string that we in turn have to decode back into a float again) would be ideal.  The reason we use the KSPField, KSPEvent, and KSPAction systems to expose PartModules to kOS scripts is that these are universally a system all PartModules use (so we don't have to write hundreds of different wrapper classes around the hundreds of different kinds of PartModules in the game).   And they let the players' scripts use names they can learn by just looking at the game UI.  Exposing a KSPfield value on the PAW that is readable as a float (not as a string) would be the easiest from our view, as it fits in with what we already do.  Also, if it's a KSPField, BUT it is marked as being hidden from the gui, then what will happen (unless we code a special exception case just for this one instance) is that kOS will still deny a script from seeing it, again under the design principle that any method or field that  isn't explicitly exposed to the player is one kOS assumes it wasn't given permission to show to the player by the author of the PartModule.

Here's an example of what is done with the mod LaserDist - a mod that was designed specifically with kOS in mind.  It exposes a "distance" KSPField on the PAW for kOS to be able to read with its generic PartModule KSPField scraping code.  The field *does* have some string formatting to the display, *but* that formatting is done by the guiFormat attribute value instead of the field being an *actual* string.  i.e. it is defined like this:
 


        /// <summary>Distance the laser is showing to the first collision:</summary>
        [KSPField(isPersistant=true, guiName = "Distance", guiActive = true, guiActiveEditor = true, guiUnits = "m", guiFormat = "N2")]
        public float Distance = 0.0f;

Doing something like that (where the field is really a float, but just displays a string via formatting) means that while the player sees the string with the "m" attached to the end, rounded to the nearest 1/100th, the actual float value of the field is what a kOS script sees.  I have no idea if this is do-able without hassle in the servo modules, or if the string assumption is buried "too deep" in how it works.

Well, we do use UI_FloatRange already, but not with that specific code.  I didn't know about the call TryGetFieldUIControl - instead we do it in two steps:  Step 1 - Get the field. Step 2 - After getting the field, then use the UI_FloatControl that is attached to that field.  It appears that the logic to bypass the range limits of the current field and instead take the range limits from a different field are buried inside this TryGetFieldUIControl call, if I understand what you're saying.  If that is the case then we should be able to fix the problem by using that technique instead.  It will require some refactor of what we're doing,  but it if it works in a more universal way then it's worth it.  Thank you for the help, and I'll be able to try it tomorrow or the day after.

Thanks Steve, Ill dig into whats involved in changing those vs new fields vs other plans, etc.

For the second one - TryGetFieldUIControl simply replaces the "am I in flight - therefore grab the uiControlFlight logic" casue we us ethe fields in all scenes. Your probably grabbing the right thing already then from above. the minValue/maxValue/stepIncrement of the UI_FloatRange will be the correct values for the range of the  UI field, but they can/are modified in game at various points. My guess would be that kOS is scraping the attributes of the Field, or scraping the values before all the partmodules start/setup and its getting the default attrib values.

Maybe have a look for that timing? from the description above sounds like how we read/write and use the UI_FloatRange fields in many scenes, those min/max/step values are not "non-changing" by design so the attrib/initial values may not always be the same.

As an extra wrinkle/feature, the robotics things like targetExtension are actually KSPAxisFields - this has its own min/max which actually defines the valid range for controller input of the field and you could use those regardless of the UI configuration - we matched all of ours to the UI ones, but no guarantees everyone will do that - and that would involve changes that might not be the greatest for you.

See how you go with the timing and Ill get back to you on the others. Can you do me a favor and log a "feedback" in the tracker and list any fields you know are not good for you - like currentExtension on the piston - and I can bundle any work up in one go. Poke the link back here with the number. Ta

 

Share this post


Link to post
Share on other sites
Posted (edited)
4 hours ago, TriggerAu said:

For the second one - TryGetFieldUIControl simply replaces the "am I in flight - therefore grab the uiControlFlight logic" casue we us ethe fields in all scenes. Your probably grabbing the right thing already then from above. the minValue/maxValue/stepIncrement of the UI_FloatRange will be the correct values for the range of the  UI field, but they can/are modified in game at various points. My guess would be that kOS is scraping the attributes of the Field, or scraping the values before all the partmodules start/setup and its getting the default attrib values.

We get the range values *at the moment* each time the script is trying to alter the value during flight, not ahead of time.  Perhaps there's a timing issue with the fact that this is happening in FixedUpdate instead of during the GUI pass?  At any rate, I'll re-write it to use this TryGetFieldUIControl and see if that fixes it.  I'd prefer to use whatever KSP is using so that if KSP changes again like this in future versions, if I use the API they're using then I'm more likely to automatically get the right behaviour without making changes.

After I try this today, I'll make sure to put an issue in the tracker about "problem 1".  First I'll need to try it for each kind of servo part, to ensure I know which BaseServo's have the problem.  I only know for sure about Pistons, and have been using that as my sole test case so far.  I haven't tried the other servos yet.

Edited by Steven Mading

Share this post


Link to post
Share on other sites
8 hours ago, TriggerAu said:

See how you go with the timing and Ill get back to you on the others. Can you do me a favor and log a "feedback" in the tracker and list any fields you know are not good for you - like currentExtension on the piston - and I can bundle any work up in one go. Poke the link back here with the number. Ta

@TriggerAu - I have tried your advice for "Problem 2", and as far as I can tell, switching to using TryGetFieldUIControl() fixed that problem perfectly.  I don't know if it was a timing issue or not, since all I had to do was replace my call in the same place in the code with that one instead to make it work.  Thanks again for this.

As for "Problem 1" - I made an issue in the KSP bug tracker for that one, as requested.  Here's the link:
https://bugs.kerbalspaceprogram.com/issues/23003

 

Share this post


Link to post
Share on other sites
Posted (edited)
On 6/25/2019 at 1:55 PM, Steven Mading said:

@TriggerAu - I have tried your advice for "Problem 2", and as far as I can tell, switching to using TryGetFieldUIControl() fixed that problem perfectly.  I don't know if it was a timing issue or not, since all I had to do was replace my call in the same place in the code with that one instead to make it work.  Thanks again for this.

As for "Problem 1" - I made an issue in the KSP bug tracker for that one, as requested.  Here's the link:
https://bugs.kerbalspaceprogram.com/issues/23003

 

@TriggerAu

I came across this while researching a related issue, hope it's ok to do it here.  This is not related to KoS, but another mod:

This mod is for the editor only.  In there, it sets a number of PAW values.  The label on these value need to be able to change depending on one of the buttons.

Testing on KSP 1.7.2

There is a callback for each field which is initialized in the OnStart():

public override void OnStart(StartState state)
{
	base.OnStart(state);

	isEnabled = enabled = false;
	if (state == StartState.Editor) {
		initializeDoTWR();
		DoTWRChanged(null, null);
	}
}

/// <summary>
/// Field to toggle whether to calculate for TWR or dV
/// </summary>
[KSPField(
	guiName = "TWR or dV", isPersistant = true, guiActive = false, guiActiveEditor = true ), 
	UI_Toggle(scene = UI_Scene.Editor, enabledText = "Do TWR", disabledText = "Do dV")]
public bool DoTWR = Settings.Instance.DoTWR;

private void initializeDoTWR()
{
	BaseField field = Fields["DoTWR"];
	UI_Toggle tog = (UI_Toggle)field.uiControlEditor;
	tog.onFieldChanged = DoTWRChanged;
}

and then, the actual callback:

private void DoTWRChanged(BaseField field, object o)
{
	if (!DoTWR)
	{
		Fields["BodyForTWR"].guiName = "dV At";
		Fields["Atmospheric"].guiName = "Atmospheric dV";
		Fields["targetTWR"].guiActiveEditor = false;
		Fields["AutoScale"].guiName = "Auto fit to dV";
	}
	else
	{
		Fields["BodyForTWR"].guiName = "TWR At";
		Fields["Atmospheric"].guiName = "Atmospheric TWR";
		Fields["targetTWR"].guiActiveEditor = true;
		Fields["AutoScale"].guiName = "Auto fit to TWR";
	}
}

The problem is that the guiName fields are not changing when the callback is called.  The fields are defined here:

[KSPField(guiName = "smartTank_TWRAtPrompt", isPersistant = true, guiActive = false, guiActiveEditor = true ), 
UI_ChooseOption(scene = UI_Scene.Editor)]
public string BodyForTWR = Settings.Instance.BodyForTWR;

[KSPField(guiName = "smartTank_AtmosphericPrompt", isPersistant = true, guiActive = false, guiActiveEditor = true), 
UI_Toggle(scene = UI_Scene.Editor)]
public bool Atmospheric = Settings.Instance.Atmospheric;

[KSPField(guiName = "smartTank_TargetTWRPrompt", isPersistant = true, guiActive = false, guiActiveEditor = true, guiFormat = "G2"),
UI_FloatEdit(scene = UI_Scene.Editor, incrementSlide = 0.1f, incrementLarge = 1f, incrementSmall = 0.1f, minValue = 0.1f, maxValue = 10f, sigFigs = 1)]
public float targetTWR = Settings.Instance.TargetTWR;

[KSPField(guiName = "smartTank_AutoScalePrompt", isPersistant = true, guiActive = false, guiActiveEditor = true), 
UI_Toggle(scene = UI_Scene.Editor)]
public bool AutoScale = Settings.Instance.AutoScale;

I know that the callback is working, since the targetTWR guiActiveEditor is being toggled properly, but the guiName fields on the other variables are not being changed.

While I could duplicate all the variables and simply make them active or not, that is wasteful and needless if the same one can do it.

I could have sworn that I've been able to change the guiName in other mods, but not sure if that was only in the flight scene.

Thanks in advance

Edited by linuxgurugamer

Share this post


Link to post
Share on other sites

I've searched the forum and whatnot, but I haven't seen this addressed in a definitive way: How can one, through kOS, "measure" the proper acceleration on a spacecraft?

In the real world, proper acceleration is the acceleration that would be measured by an accelerometer carried along with the spacecraft. That is to say, on the launchpad it would read something close to 9.80665 m/s/s and in orbit it would read 0 m/s/s.

Reading SHIP:SENSORS:ACC doesn't do what I want because on the launchpad it reads a very small number (on the order 10^-5), while in orbit it reads 6.8 m/s/s, which I don't understand at all. If it were just equal to local g I could subtract that off, but it's not.

In the atmosphere I can approximate proper acceleration with THROTTLE * SHIP:AVAILABLETHRUST / SHIP:MASS, but if there's a way to get the actual proper acceleration I'd much rather use it.

Thanks!

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.