Jump to content

[1.3] kOS Scriptable Autopilot System v1.1.3.0


erendrake

Recommended Posts

Replying to myself here:

One of the issues on the issues tracker is to make it explicitly notice when a WHEN clause is taking too long, and abort it with an error message telling the user what's going on and why and how they have to redesign their code.

I should warn people that as this gets implemented it might appear to "slow down" some existing scripts a bit, but that would be because it's now going to be counting the triggers like LOCK and WHEN statements toward the 100 instructions per update limit. In the past what you got was 100 instructions per Update, PLUS ALSO whatever your WHEN and LOCK and ON statements were doing, which wasn't being tracked as part of that 100. The new design will make the program behave much more predictably, in terms of how many milliseconds of runtime it steals from KSP itself being mostly the same no matter how you wrote your code, and I might raise CONFIG:IPU to a bigger default to handle the fact that it would now be counting everything.

Link to comment
Share on other sites

Here is a little feature request:

One of my SSTO designs (using FAR) requires variable flap settings based on air speed measured in mach numbers.

It would be nice to have a function returning the speed of sound for a vessel's location.

Link to comment
Share on other sites

I think you can just lock a variable to some calculation in one line. I don't know how FAR calculates speed of sound at different altitudes, but once I know that, I can probably make it.

Link to comment
Share on other sites

-lots of things-

Thank you for the thorough explanation. I've been beating my head against a wall trying to learn orbital mechanics and programming logic at the same time. I'm not sure what was causing the NaN errors, but they all stopped on their own when I removed "SET CONFIG:SAFE TO FALSE.", perhaps because I'd unknowingly fixed a bug in my scripts at that point. As for the "STATUS" variable, is there a list of all the possible values of it? It took me a bit to figure out that I also need to check for "PRELAUNCH" in scripts that detect whether a vessel is landed. Incidentally, I found that checking altitude for whether a vessel is on the ground is unreliable at best - I had one landed rocket with a "radar altitude" of over 100 meters because of its height.

And as for the problem with fuel and staging...well, it was working for a bit, then started breaking sporadically. Then I discovered it was because engines would stop burning at random points - I had solid boosters on the same rocket that stopped at 0, 0.01, and 0.06 fuel during different launches using the same launch autopilot. I adjusted my script, but it still only works half the time, and I know it's not just because of the fuel levels now. I think having two separate conditions check for the same thing within a loop, one of which relies upon a flag set by the other, is a bad idea. My guess is that sometimes one triggers before the other in the wrong order, so the rocket no longer thinks it has solid boosters the instant they run dry and never stages. Anyway, I suppose a slightly wordier alternative script will fix the problem.

EDIT: I did indeed fix the staging issue by making it do the fuelType check just before the main UNTIL loop and making it only change fuelType again after staging. Of course, now my ascent autopilot is over 3000 bytes. I suppose one way to shrink a program is to split it into separate programs to function as modules to do repetitive code.

Edited by amiavamp
Link to comment
Share on other sites

I found that checking altitude for whether a vessel is on the ground is unreliable at best - I had one landed rocket with a "radar altitude" of over 100 meters because of its height.

This is happening because every position and distance is measured from the center of mass of the vessel containing the kOS part. When you ask "what is my altitude?" that immediately raises the question, "do you mean the altitude of your landing legs? The altitude of your control capsule?..." kOS always picks the center of mass of the ship, because that's what the native KSP system uses.

Rather than call that a problem, I think it's fine that it does this, and the real problem is that you don't have a way to detect "what is the shape and size of this vessel?" The real fix isn't to change how altitude is reported, but to give you the ability to query "How many meters away from the center of mass is the bottom of my ship?" Right now that can't be done, but work is being done on features that would let you do that (mainly the ability to get the position and orientation of each part individually).

And as for the problem with fuel and staging...well, it was working for a bit, then started breaking sporadically. Then I discovered it was because engines would stop burning at random points - I had solid boosters on the same rocket that stopped at 0, 0.01, and 0.06 fuel during different launches using the same launch autopilot. I adjusted my script, but it still only works half the time, and I know it's not just because of the fuel levels now. I think having two separate conditions check for the same thing within a loop, one of which relies upon a flag set by the other, is a bad idea. My guess is that sometimes one triggers before the other in the wrong order, so the rocket no longer thinks it has solid boosters the instant they run dry and never stages. Anyway, I suppose a slightly wordier alternative script will fix the problem.

Other users have reported this problem lately and we've discovered that it's not a bug in kOS but rather it's a weird buggy behavior in KSP itself that kOS is exposing you to in ways the manual flying interface tends to hide. What seems to be happening is that your animation frame rate takes a sliver of time, and KSP refuses to burn fuel if the fuel won't last the entire duration of that animation frame. So hypothetically if you have a choppy framerate of say 10 frames/sec, and the remaining tiny last drops of fuel, at the current throttle setting, would only last for, say, 1/20th of a second, then KSP flames out the engine now, since there's not enough fuel

to last one whole animation frame. Thus the last fuel never gets consumed. You don't notice it when flying manually because values tend to be displayed rounded off so much that the little sliver of fuel gets hidden from sight.

Therefore, a more reliable check for whether an engine is starved is to query the engine's :FLAMEOUT boolean value. It will return TRUE when the fuel is in that scenario where there's fuel for the engine, but not enough to last an entire animation frame.

EDIT: I did indeed fix the staging issue by making it do the fuelType check just before the main UNTIL loop and making it only change fuelType again after staging. Of course, now my ascent autopilot is over 3000 bytes. I suppose one way to shrink a program is to split it into separate programs to function as modules to do repetitive code.

I've never been happy with the way the code size is counted. Basically, these two programs have different source code size, but are identical after being compiled:


// This is a one-line program with
// a lot of verbose comments,
// and it uses long variable names and
// a lot of indenting spaces:
DECLARE PARAMETER this_is_my_long_variable_name
PRINT "val squared is " + this_is_my_long_variable_name ^ 2.


DECLARE PARAMETER x. PRINT "val squared is "+x^2.

Once they're compiled, they're the same size. It's rather annoying to have to "pay" for writing good variable names and using readable indentation.

Given that kOS actually compiles your script code to a pseudo-bytecode machine language in its head when you issue the RUN command, I've been contemplating the notion of letting users actually store pre-compiled "object code" files on the craft's local volume and running from them instead of from the full source files.

Link to comment
Share on other sites

I've never been happy with the way the code size is counted. Basically, these two programs have different source code size, but are identical after being compiled:


// This is a one-line program with
// a lot of verbose comments,
// and it uses long variable names and
// a lot of indenting spaces:
DECLARE PARAMETER this_is_my_long_variable_name
PRINT "val squared is " + this_is_my_long_variable_name ^ 2.


DECLARE PARAMETER x. PRINT "val squared is "+x^2.

Once they're compiled, they're the same size. It's rather annoying to have to "pay" for writing good variable names and using readable indentation.

Given that kOS actually compiles your script code to a pseudo-bytecode machine language in its head when you issue the RUN command, I've been contemplating the notion of letting users actually store pre-compiled "object code" files on the craft's local volume and running from them instead of from the full source files.

Hmmm... kOS + RemoteTech = Compile In the Archive, upload the compiled code. Minimize time/elec for comms. Also, new CPUs that can compile, for stations/bases where there's more resources to code and compile the programs before giving the object code to the flight computers (assume Flight Center already has enough CPU power to compile Kerbin-side). Luverly! Only allow in-game edits when there's a comms connection to a compiler unit, or from a vessel with a compiler unit configured. Limit out-of-game edits to Kerbin Archive only? Hrmm... That might require more archive-level storage, for the compiler CPUs, perhaps a drive array of some sort? Unlimited on Kerbin, but you have to build your infrastructure off-world. Long term goals...

Link to comment
Share on other sites

Is there a way to check if the user has a target set? I'm trying to fetch some data from a target, and I'd like to build in a graceful exit in case the user forgot to set his target.

On another note, it seems that kOS thinks that 10^-1 equals 0, which it obviously doesn't.

Link to comment
Share on other sites

Is there a way to check if the user has a target set? I'm trying to fetch some data from a target, and I'd like to build in a graceful exit in case the user forgot to set his target.

At the moment, no. Because an unset target has value NULL, and kOS doesn't have a way to check against whether or not something is NULL, which is a hole in the language I'd like to see fixed.

On another note, it seems that kOS thinks that 10^-1 equals 0, which it obviously doesn't.

Temp fix for now: explicitly state that the arguments are floating point, like so: 10.0 ^ -1.0

The problem is that when both operands are integer, it's assuming the output should be integer, and thus rounding. You're getting 0 because it's rounding the value of 0.1 down.

long term fix - I'd like to change all math operations so they find out whether the result will be a round number or not, and then decide whether to return it as an integer or floating point based on that, rather than based on the input operands' types. This is an area of the code I haven't touched much and the person who has has been very busy and out of contact for a while, making me reluctant to stick my hands in his code and change everything without buy-in from him.

Link to comment
Share on other sites

At the moment, no. Because an unset target has value NULL, and kOS doesn't have a way to check against whether or not something is NULL, which is a hole in the language I'd like to see fixed.

Ah, that's a shame. I guess I'll just set a simple target like the mun during initialization to avoid the problem.

Temp fix for now: explicitly state that the arguments are floating point, like so: 10.0 ^ -1.0

The problem is that when both operands are integer, it's assuming the output should be integer, and thus rounding. You're getting 0 because it's rounding the value of 0.1 down.

long term fix - I'd like to change all math operations so they find out whether the result will be a round number or not, and then decide whether to return it as an integer or floating point based on that, rather than based on the input operands' types. This is an area of the code I haven't touched much and the person who has has been very busy and out of contact for a while, making me reluctant to stick my hands in his code and change everything without buy-in from him.

Thanks, that worked.

Link to comment
Share on other sites

Hmmm... kOS + RemoteTech = Compile In the Archive, upload the compiled code. Minimize time/elec for comms. Also, new CPUs that can compile, for stations/bases where there's more resources to code and compile the programs before giving the object code to the flight computers (assume Flight Center already has enough CPU power to compile Kerbin-side). Luverly! Only allow in-game edits when there's a comms connection to a compiler unit, or from a vessel with a compiler unit configured. Limit out-of-game edits to Kerbin Archive only? Hrmm... That might require more archive-level storage, for the compiler CPUs, perhaps a drive array of some sort? Unlimited on Kerbin, but you have to build your infrastructure off-world. Long term goals...

I've often thought that a neat feature might be to create a "space center mainframe" kOS CPU that lives inside one of the ground buildings, conceptually, who's "local drive" is the archive. You could use it to run kOS code, so long as that kOS code doesn't do anything that requires ship-based information like your velocity, your current fuel, etc, because the CPU wouldn't be associated with a vessel. This would let you do things like write code that tries to calculate a good launch window for you, and tells you the date/time that you should probably launch your mission if your goal is to get to planet so-and-so. It would also let you use the infinite storage to run something too complex for the remote probe, and just transmit the final answer to the remote probe. It always seemed a bit silly to me that the way kOS is working now, you'd have to first put your vessel on the launchpad, then run the code to find the launch window, then wait for several months with the vessel on the launchpad, then launch it. A real space program would be doing those calculations on a ground-based computer first in order to learn when to put the vessel on the launchpad.

The only reason this isn't done yet (well, besides kOS being a neat huge project with a "would be nice" list a mile long already) is that so much of how the system works is built on the assumption that all cpu's are contained in a KSP part, which is contained in a KSP vessel. If you tried to run code that said, for example, "PRINT SHIP:VELOCITY" on a kOS CPU that isn't ON a ship, I suspect it wouldn't just give a nice error message, but would crash the Csharp code because it presumes there will always be a SHIP no matter what. Trying to divorce the system from that assumption would be a lot of work and there's bigger problems to tackle right now. But it is something for the future I'd like to see "some day".

As a stopgap, I've considered building a rover vessel that has a kOS CPU on it and an antenna array, launching it at KSP, and just driving it over to the buildings and parking it there and leaving it for the rest of my campaign. Then it could be my "ground" mainframe, and could participate in remoteTech transmissions and the like, and it would be close enough to KSC that it would always be in range of the archive.

Link to comment
Share on other sites

I haven't done any programming until now and I'm having difficulty doing what I want with this great mod, I've read most of the wiki and I can get a rocket to orbit but the particular thing I'd like to control is a walker that walks by IR joints activated with various action groups with various timings. I've got the Toggle AG's and Wait's in between them working as a string of commands that takes two steps forward. I left AG1 blank to use as an input to make it take repeated two step cycles. I've looked at ON AG1, or WHEN AG1=1 THEN, UNTIL, and I'm perplexed.

How would I make a script that runs my string of walk commands in a loop when AG1 is pressed once and stops when it's pressed again (must complete the last loop fully), but doesn't stop there and resets so it's waiting for the next AG1 to start looping the walk commands again (continually running while the vessel is active).

Any pointers will be greatly appreciated, cheers Darren9.

Link to comment
Share on other sites

BOOLEAN TOGGLE FIELDS:

These are variables that behave like boolean flags. They can be True or False, and can be set or toggled using the "ON" and "OFF" and "TOGGLE" commands. Many of these are for action group flags. NOTE ABOUT ACTION GROUP FLAGS: If the boolean flag is for an action group, be aware that each time the user presses the action group keypress, it toggles the action group, so you might need to check for both the change in state from false to true AND the change in state from true to false to see if the key was hit.


set stop to false.
until stop = true {

[INDENT]if (ag01 = true) {
[/INDENT]


[INDENT=2]do stuff
[/INDENT]
[INDENT=2]if (ag01 = false) {
[/INDENT]
[INDENT=3]set stop to true;
[/INDENT]
[INDENT=2]}.[/INDENT]

[INDENT]} else {
[/INDENT]


[INDENT=2]set stop to true.
[/INDENT]

[INDENT]}.
wait 0.001.
[/INDENT]


}.

Edited by fathed
Link to comment
Share on other sites

BOOLEAN TOGGLE FIELDS:

These are variables that behave like boolean flags. They can be True or False, and can be set or toggled using the "ON" and "OFF" and "TOGGLE" commands. Many of these are for action group flags. NOTE ABOUT ACTION GROUP FLAGS: If the boolean flag is for an action group, be aware that each time the user presses the action group keypress, it toggles the action group, so you might need to check for both the change in state from false to true AND the change in state from true to false to see if the key was hit.

Furthermore, this is what the ON trigger is designed to do, and was made that way specifically because of the way action group booleans work (which is the way they're implemented in KSP itself).

You might have wondered why there even is an ON statement, given that you can write "WHEN AG1=TRUE THEN...".

The reason is that ON triggers on any CHANGE TO the boolean variable it mentions: from false to true or from true to false.

ON AG1 { dostuff. }

is the same as:

set previous_AG1 to AG1.

WHEN AG1 <> previous_AG1 THEN {

set previous_AG1 to AG1.

dostuff.

}.

But without you having to mess about with tracking previous_AG1 yourself. The ON command does that tracking internally for you.

Link to comment
Share on other sites

His advice is much better than mine, use it, not mine.
Unfortunately I couldn't make that work, there's a ";"it doesn't like (I changed it to a "."), and "ag01 isn't defined as a variable" (I changed those to "ag1") and then it runs but exits immediately. And I still wouldn't have a clue how to include the "ON AG1".
Furthermore, this is what the ON trigger is designed to do, and was made that way specifically because of the way action group booleans work (which is the way they're implemented in KSP itself).

You might have wondered why there even is an ON statement, given that you can write "WHEN AG1=TRUE THEN...".

The reason is that ON triggers on any CHANGE TO the boolean variable it mentions: from false to true or from true to false.

ON AG1 { dostuff. }

is the same as:

set previous_AG1 to AG1.

WHEN AG1 <> previous_AG1 THEN {

set previous_AG1 to AG1.

dostuff.

}.

But without you having to mess about with tracking previous_AG1 yourself. The ON command does that tracking internally for you.

I understand "ON AG1 { dostuff. }", but not how I'd make it loop the "do stuff" until another AG1 and then go back to the first "ON AG1".

And thanks for both of your help.

Link to comment
Share on other sites

I understand "ON AG1 { dostuff. }", but not how I'd make it loop the "do stuff" until another AG1 and then go back to the first "ON AG1".

I almost thought I understood what you meant until I got to the phrase "then go back to the first ON AG1" - I have no idea what you mean by that.

If I understand what you were talking about earlier, you have a routine that does two robotic footsteps. You want it to do that each time a user hits AG1, then wait for the user to hit AG1 again before it takes another two steps. Is this correct? Or did you want it to continue taking two steps over and over until AG1 is hit again to stop it?

The main trick you have to realize is that triggers like ON and WHEN are not supposed to contain long running loops. This is because they run all the way to the end before kOS finishes its animation frame update, and the entire KSP universe is frozen in place until they finish. Thus you can't really do any piloting or controlling in loops with them, because the universe won't react to your control inputs until you finish the body of the ON or WHEN and allow the KSP animation to resume. This is not intuitively obvious from the language definition, and furthermore it is a change from the way it used to be implemented back when Kevin designed the system. If you got your information from the out of date Wiki it probably won't mention this because back in version 0.9 it wasn't the case yet. I've implemented a new error check that catches it when you try to run long-lasting code inside a trigger, but it's not going to be released for a while.

Contrast that with main body code. In main body code, the CPU only runs as much of your code as it thinks it's prudent to run within one animation frame of KSP, and then pauses your script program, lets the rest of KSP, and all the other KSP mods, do their update work for that frame of animation, and on the next animation frame, it continues your program where it left off - so long running loops are safe in the main body of the code, but not in ON or WHEN.

So what you can do is run the main loop in the main body of the code, but use triggers like On and WHEN to just fiddle with variable settings to affect what will happen in the main body. The first example program below does this.

For these two examples, replace the word TAKE_TWO_STEPS with whatever the lines of code are that you've written that you said takes two steps with your rover.


// This is a version where AG1 is supposed to just take two steps once, and you have to keep hitting AG1 again and again
// to keep taking steps over and over:

SET doSteps to false.
ON AG1 {
SET doSteps to true.

PRESERVE. // Causes the ON statement to remain in place for next time.
// (The default is that ON and WHEN will only trigger once and then delete
// themselves, unless you explicitly tell them not to with this keyword.).
}.
SET done to false.
UNTIL done {
if doSteps {

TAKE_TWO_STEPS.

SET doSteps to False. // will not trigger again until AG1 hit again.
}.
}.
// Note, I didn't set done to true anywhere. This is an infinite loop
// until you decide how you want it to end, and implement it by
// doing a SET DONE TO TRUE somewhere.


// This is a version where the rover is supposed to continue walking continually
// as long as AG1 is on. When AG1 is toggled again to off, the rover stops
// until AG1 is hit again:

SET done to false.
UNTIL done {
if AG1 { // The first time you hit 1, AG1 becomes True. Hit it again and it becomes false (and the walking stops).

TAKE_TWO_STEPS.

}.
}.
// Note, I didn't set done to true anywhere. This is an infinite loop
// until you decide how you want it to end, and implement it by
// doing a SET DONE TO TRUE somewhere.

Edited by Steven Mading
Link to comment
Share on other sites

Just wanted to say thanks for implementing the vectordraw code. At first I had trouble finding a use for it, but as soon as I started to work on my new launch script it helped tremendously to visualize what my code was actually doing:

4c18xVV.png

Link to comment
Share on other sites

I almost thought I understood what you meant until I got to the phrase "then go back to the first ON AG1" - I have no idea what you mean by that.

~snip~

It was the second example I was after, understanding the first will also be very useful. Thanks for taking the time to explain it to me :)
Link to comment
Share on other sites

Just wanted to say thanks for implementing the vectordraw code. At first I had trouble finding a use for it, but as soon as I started to work on my new launch script it helped tremendously to visualize what my code was actually doing:

I noticed in your screenshot that Kerbin looks like Earth, with the camera aimed at Indonesia. Which mod are you using that does this? I'd heard people had had all sorts of problems getting RSS to work with kOS.

Link to comment
Share on other sites

I noticed in your screenshot that Kerbin looks like Earth, with the camera aimed at Indonesia. Which mod are you using that does this? I'd heard people had had all sorts of problems getting RSS to work with kOS.

Yea that's RSS. Using the latest version of both that and kOS and I'm having no issues at all.

Link to comment
Share on other sites

My experience worth RO and KoS was km_gimbal that was preventing kos from working properly with prior RO installs - KoS autopilot wouldn't invoke gimbal control. Aerodynamic, RCS, and pod torque worked fine.

.24 RO has removed km gimbal as stock has added roll control to default.

This isn't definitive or anything, just my observations.

Link to comment
Share on other sites

My experience worth RO and KoS was km_gimbal that was preventing kos from working properly with prior RO installs - KoS autopilot wouldn't invoke gimbal control. Aerodynamic, RCS, and pod torque worked fine.

.24 RO has removed km gimbal as stock has added roll control to default.

This isn't definitive or anything, just my observations.

Note that I'm not using RO, but instead a homebrewed set of configs. So if people are having trouble with kOS + RSS it might just be some incompatibility between RO and kOS.

Link to comment
Share on other sites

Guest
This topic is now closed to further replies.
×
×
  • Create New...