Jump to content

[1.3] kOS Scriptable Autopilot System v1.1.3.0


erendrake

Recommended Posts

10 hours ago, DBowman said:

Can anyone tell me the best way to save some data (maneuver nodes) from one vessel so I can pick them up from another? The mental model I had was like a dictionary of flight plans at KSC that vessels could access. I see vessels can message each other but that doesn't seem to fit naturally for what I wanted to do. I see there are file manipulation functions - maybe I have to use those if I want things to persist as I switch vessel to vessel?

@dBowman you should look into https://ksp-kos.github.io/KOS/commands/serialization.html.

I can't verify it right now, but i'm pretty sure that nodes are serializable. So saving a node will look like

Quote

writejson(MyNde,"0:/savedNodes/"+NodeName).

And loading it up :

Quote

set MyNode to readjson("0:/SavedNodes/"+NodeName).

You can send the Node by message too. However I don't think it will help you very much. Results of the node will be different from a vessel to another (same DV will not put you in the same orbit with different mass). Even the smallest differences in execution with the same vessel will accumulate over time. 

Instead you should save all orbits and figuring out a script that creates a node for for going from one orbit to the other. 

Edited by Chabadarl
add precisions on errors...
Link to comment
Share on other sites

@Chabadarl thanks those json functions look great. re the orbit vs node; good point to keep in mind - for what I have in mind I think the node will be okay - but for other things I can see wanting the orbits.

Does anyone know if there is a way to see if a variable is initialized / define yet? so I can do like:

if plans is NULL { set plans to lexicon()  }

if plans[somename] is NULL { something } ELSE { something else}

Link to comment
Share on other sites

@DBowman 

You better initialize plans at the start of your script or before the function declaration :  set plans to lexicon(). 

My best practice is to use a specific file for each librairy of functions with a local declaration inside. So the functions will share the same context but you can still use the name "plans" in any other script without breaking everything and you can extract your variable using a return statement (like whateArethePlans example).

 

{
  //declare your variables
  local plans is lexicon().

  //then all the function that use the same context
  function addPlan{
     //some code
  }

  function loadPlan{
     //some code
  }
  function whatArethePlans{
    return plans. 
  }
}

for :
    if plans[somename] is NULL { something } ELSE { something else}
you can use :

    if plans:haskey(somename) { something. } else { something_else.}

Edited by Chabadarl
typos
Link to comment
Share on other sites

5 hours ago, DBowman said:

@Chabadarl thanks those json functions look great. re the orbit vs node; good point to keep in mind - for what I have in mind I think the node will be okay - but for other things I can see wanting the orbits.

Does anyone know if there is a way to see if a variable is initialized / define yet? so I can do like:

if plans is NULL { set plans to lexicon()  }

if plans[somename] is NULL { something } ELSE { something else}

There is no concept of null in kOS (for better or worse, that debate has been done many times).  However you can find if a variable identifier is define using the defined keyword.

Link to comment
Share on other sites

7 hours ago, dafidge9898 said:

How come when I have the program print target:angularmomentum it prints a zero vector? Is it supposed to be like that? If I switch over to that ship and have it print its angular momentum it does not print a zero vector.

I don't know if this is one of KSP's many active-vessel-only values, but assuming it isn't: is the target within the physics bubble of the craft where the program is running i.e. is it LOADED and UNPACKED?

Link to comment
Share on other sites

Hi All, 

Has anyone managed to compile a working version against pre-release 1.2? I'd just like to test it out and play with v1.2 but without KOS it's just not as much fun :-)

Thanks.

Edited by dz006
Link to comment
Share on other sites

On 10/4/2016 at 8:47 PM, dafidge9898 said:

How come when I have the program print target:angularmomentum it prints a zero vector? Is it supposed to be like that? If I switch over to that ship and have it print its angular momentum it does not print a zero vector.

Is the target vessel more than 300 meters away but less than 2.5 km away?

If so, then this is probably an effect of the weird fact that the stock game sets the loading distance to 2200m, but sets the unpack distance to 300m.  There's a distance window between 300 and 2200 where a vessel is loaded but not unpacked.  In this state you can *look* at the vessel, but can't actually control it.  (It populates the parts of the vessel for visual appearance purposes, but doesn't fully activate them "all the way" in the physics engine, I believe.)  In this state chances are the vessel can't rotate either thus the angularmomentum being zeroed out.

Link to comment
Share on other sites

6 hours ago, Technical Ben said:

Ah. Is that a newer behaviour of the engine? I remember back in the day controlling around 6 landers with MechJeb at one go (landing a lighting system for my base/lander in one go :D ).

Working around a 300m limit would be rather difficult. :(

It isn't a new limitation, but it was tweaked a few KSP versions ago.  Probably the most noticeable difference now is that the value changes depending on "situation" (landed, flying, orbiting, etc.).  There is also a buffer distance before re-packing or unloading, so that a vessel right on the limits of the distance isn't constantly switching between the different load/packed states.

Link to comment
Share on other sites

I'm trying to write code to move IR Servos individually, by reading the documentation i got the following working:

for s in ADDONS:IR:ALLSERVOS
{
    if (s:name = "LegA1L")
    {
        s:moveleft().
    }    
}

But i'd like to know how to target a Servo directly by it's name, instead of scanning through all of them.

 

Edit:
I did figure out this instruction:     addons:ir:allservos[1]:moveleft().
How can i do this by addressing the servo by it's name instead of [1]?
 

Edited by Leralite
Link to comment
Share on other sites

@Leralite  here we go :

//The initialisation has to be done just once at the start of your program
set MyServos to lexicon(). 
for s in addons:ir:allservos 
{ MyServos:add(s:name,s).}

//then you can use in your code
MyServos["TheServoThatGoesToTheLeft"].moveleft().

 

Edited by Chabadarl
Link to comment
Share on other sites

14 hours ago, Leralite said:

But i'd like to know how to target a Servo directly by it's name, instead of scanning through all of them.

While I like @Chabadarl's solution above better, you could alternatively use the partservos suffix and kOS name tags to look things up.  If you can safely assume that there is one and only one part with the given tag, and that the part has one and only one servo, you could use the following code:

set servo to addons:ir:partservos(ship:partstagged("name")[0])[0].
servo:moveleft().

But there are two problems with this method: the syntax is complicated (making it harder to read and easier to break) and it requires the construction of multiple lists that are immediately discarded.  Both issues are fixed with the previous suggestion.  Syntax to access the lexicon is very clean, as is the syntax to construct the lexicon.  The only complication is that the lexicon uses a one to one relationship, and neither IR group names nor kOS name tags are guaranteed to be unique part identifiers.  At that point, you can choose whichever method you find easier to label/identify.  If you go with group names though, I'd recommend storing a reference to the group itself instead of the part, that way you can seamlessly transition to supporting multiple servos:

//The initialisation has to be done just once at the start of your program
set MyServoGroups to lexicon(). 
for g in addons:ir:allgroups
{ MyServoGroups:add(g:name,g).}

//then you can use in your code
MyServoGroups["TheGroupThatGoesToTheLeft"].moveleft().
Link to comment
Share on other sites

Not really sure what's going on here - I need someone to take a look.

https://www.youtube.com/watch?v=5SVr5ZLRrRg

The GUIDs are being generated by the following code:

https://github.com/dsonbill/kOSPropMonitor/blob/master/src/kOSPropMonitor/kPMAPI.cs#L53 (which is called by kPMCore in its update)

and https://github.com/dsonbill/kOSPropMonitor/blob/master/src/kOSPropMonitor/kOSMonitor.cs#L116 (which is called by KSP or RPM)

Also not sure why two with the same vessel id are already showing up per-vessel. They are added on object creation to the vessel_registry in kPMCore and called from a list on the contained object.
Their update function won't be called unless they added to kPMCore's vessel_registry with their shared.Vessel.id.

I'm going to restructure how the values are set and accessed soon, but I'll still need to get the vessel track object via shared.vessel.id.

 

EDIT: So I guess the shared objects are passed in as the stuff is called or something, because it just kind of works after restructuring to a saner approach.

See https://github.com/dsonbill/kOSPropMonitor/blob/master/src/kOSPropMonitor/kPMAPI.cs for sane approach, and look at last commit for errors. Everything works perfect now.

 

By the way, is IPC possible in stock kOS?

uEn5j5Z.png

l8Nviuk.png

 

Consumer.ks:

set flags to addons:kpm:flags.

flags:setstate(0, true).
flags:setstate(1, true).
flags:setstate(7, true).
flags:setstate(8, true).

flags:setlabel(0, "RUNMODE:").
flags:setlabel(7, "ID:").
flags:setlabel(8, "0").

lock runmode to flags:getlabel(1).
set id to "0".

until runmode = "00"
{
    if flags:getlabel(5) = id
    {
        if runmode = "0"
        {
            clearscreen.
            print("CPU Waiting...").
        }
        
        if runmode = "1"
        {
            clearscreen.
            print("Doing Something!").
        }
    }
} 

 

Edited by dsonbill
Link to comment
Share on other sites

50 minutes ago, hvacengi said:

I don't know what that means, so I can neither confirm nor deny.  What does the acronym stand for?

IPC is interprocess communication, but I guess this is interprocessor communication, heh.

Link to comment
Share on other sites

I'm having some weird behavior when writing a 'node burner'. Basically I check 'targetAp vs ship:orbit:Ap' and when it's small enough ( < 10 m ) I stop (0 throttle). Then I unlock steering and throttle and 'wait 1.' after the wait the difference in the Aps is 100 m or more.

The way it's working now is 'close enough' - but I worry that it's only close enough 'by accident' of my ship & orbit & node magnitude and I'd like to understand where the discrepancy is coming from. Does anyone have any ideas? a fragment of the code is below with **** PRINTS showing what it prints when

until done {
    set epsAp to (targetAp - ship:orbit:apoapsis ).
    ...
    if ship:orbit:apoapsis >= targetAp {
        print "Crash Stop Burn Ap eps: " + (targetAp - ship:orbit:apoapsis ).
        lock throttle to 0.
        break.
    }
    if epsAp < 500 {
            print "Finalizing Burn Ap eps: " + epsAp + " rdv: "+round(node:deltav:mag,1).
        wait until (targetAp - ship:orbit:apoapsis ) < 10. // why is this small then final is not?
            print "Waited Ap eps: " + (targetAp - ship:orbit:apoapsis ).
            // **** PRINTS -25
        lock throttle to 0.
            print "End Burn Ap eps: " + (targetAp - ship:orbit:apoapsis ).
            // **** PRINTS -25
        set done to True.
    }
}
    print "Done1 Ap eps: " + (targetAp - ship:orbit:apoapsis ).
    // **** PRINTS -25
unlock steering.
unlock throttle.
    print "Done2 Ap eps: " + (targetAp - ship:orbit:apoapsis ).
    // **** PRINTS -25
wait 1.
    print "Done Ap eps: " + (targetAp - ship:orbit:apoapsis ).
    // **** PRINTS -224
    print "Done p eps: " + (targetPeriod - ship:orbit:period).
remove node.
SET SHIP:CONTROL:PILOTMAINTHROTTLE TO 0.
    print "Done Ap eps: " + (targetAp - ship:orbit:apoapsis ).
    // **** PRINTS -224
    print "Done p eps: " + (targetPeriod - ship:orbit:period).

 

Link to comment
Share on other sites

 

30 minutes ago, DBowman said:

I'm having some weird behavior when writing a 'node burner'. Basically I check 'targetAp vs ship:orbit:Ap' and when it's small enough ( < 10 m ) I stop (0 throttle). Then I unlock steering and throttle and 'wait 1.' after the wait the difference in the Aps is 100 m or more.

The way it's working now is 'close enough' - but I worry that it's only close enough 'by accident' of my ship & orbit & node magnitude and I'd like to understand where the discrepancy is coming from. Does anyone have any ideas? a fragment of the code is below with **** PRINTS showing what it prints when


until done {
    set epsAp to (targetAp - ship:orbit:apoapsis ).
    ...
    if ship:orbit:apoapsis >= targetAp {
        print "Crash Stop Burn Ap eps: " + (targetAp - ship:orbit:apoapsis ).
        lock throttle to 0.
        break.
    }
    if epsAp < 500 {
            print "Finalizing Burn Ap eps: " + epsAp + " rdv: "+round(node:deltav:mag,1).
        wait until (targetAp - ship:orbit:apoapsis ) < 10. // why is this small then final is not?
            print "Waited Ap eps: " + (targetAp - ship:orbit:apoapsis ).
            // **** PRINTS -25
        lock throttle to 0.
            print "End Burn Ap eps: " + (targetAp - ship:orbit:apoapsis ).
            // **** PRINTS -25
        set done to True.
    }
}
    print "Done1 Ap eps: " + (targetAp - ship:orbit:apoapsis ).
    // **** PRINTS -25
unlock steering.
unlock throttle.
    print "Done2 Ap eps: " + (targetAp - ship:orbit:apoapsis ).
    // **** PRINTS -25
wait 1.
    print "Done Ap eps: " + (targetAp - ship:orbit:apoapsis ).
    // **** PRINTS -224
    print "Done p eps: " + (targetPeriod - ship:orbit:period).
remove node.
SET SHIP:CONTROL:PILOTMAINTHROTTLE TO 0.
    print "Done Ap eps: " + (targetAp - ship:orbit:apoapsis ).
    // **** PRINTS -224
    print "Done p eps: " + (targetPeriod - ship:orbit:period).

 

The decrease of power of your engine is not instantanous. Nothing to do with your coding but with kerbal physics...

I suggest to go with you way of writing the script:

wait until (targetAp - ship:orbit:apoapsis ) < 400. 
            print "Waited Ap eps: " + (targetAp - ship:orbit:apoapsis ).
        lock throttle to 0.5.
wait until (targetAp - ship:orbit:apoapsis ) < 300. 
            print "Waited Ap eps: " + (targetAp - ship:orbit:apoapsis ).
        lock throttle to 0.3.
wait until (targetAp - ship:orbit:apoapsis ) < 200. 
            print "Waited Ap eps: " + (targetAp - ship:orbit:apoapsis ).
        lock throttle to 0.2.
wait until (targetAp - ship:orbit:apoapsis ) < 100. 
            print "Waited Ap eps: " + (targetAp - ship:orbit:apoapsis ).
        lock throttle to 0.1.

And tuning the values tho decelerate gently...

 

 

However I rather use the following :

until done {
    set epsAp to (targetAp - ship:orbit:apoapsis ).
    lock throttle to min(0.9*time4man(n:burnvector:mag), 1). 
//in the last 1.1 seconde of the burn the throtlle decrease so that the burn will last 2,5 secondes but the power will actually be 0 at the end.

    ...
    if ship:orbit:apoapsis >= targetAp {
        print "Crash Stop Burn Ap eps: " + (targetAp - ship:orbit:apoapsis ).
        lock throttle to 0.
        break.
    }
    if epsAp < 10 {
        print "Waited Ap eps: " + (targetAp - ship:orbit:apoapsis ).
        lock throttle to 0.
        set done to True.
    }
}

    print "Done1 Ap eps: " + (targetAp - ship:orbit:apoapsis ).
unlock steering.
unlock throttle.
    print "Done2 Ap eps: " + (targetAp - ship:orbit:apoapsis ).
wait 1.
    print "Done Ap eps: " + (targetAp - ship:orbit:apoapsis ).
    print "Done p eps: " + (targetPeriod - ship:orbit:period).
remove node.
SET SHIP:CONTROL:PILOTMAINTHROTTLE TO 0.
    print "Done Ap eps: " + (targetAp - ship:orbit:apoapsis ).

 

for info my time4man function is:

function orb_time4man {
  parameter dv.
  local enon is list().
  enon:clear.
  local enon_thrust is 0.
  local enon_isp is 0.
  local ens_thrust is 0.
  local ens_isp is 0.
  list engines in myengines.

  for en in myengines {
    if en:ignition = true and en:flameout = false {
      enon:add(en).
      set enon_thrust to enon_thrust + en:availablethrust.
      set enon_isp to enon_isp + en:isp.
    }
  }
  if enon_thrust = 0 or enon_isp = 0 {
    notify("no power!").
    return 0.
  }
  else {
    local f is enon_thrust * 1000.  // engine thrust (kg * m/s²)
    local m is ship:mass * 1000.        // starting mass (kg)
    local e is constant():e.            // base of natural log
    local p is enon_isp/enon:length.               // engine isp (s) support to average different isp values
    local g is ship:orbit:body:mu/ship:obt:body:radius^2.    // gravitational acceleration constant (m/s²)
    return g * m * p * (1 - e^(-dv/(g*p))) / f.  // thanks wikipedia I guess.
  }
}  

 

Link to comment
Share on other sites

13 hours ago, Chabadarl said:

The decrease of power of your engine is not instantanous. Nothing to do with your coding but with kerbal physics...

Ahhh so you think it's just 'control lag' and/or 'engine spooling down'? My throttle is at about 0.2 when I zero it, for sure I can / should tail that down more gently. Thanks for you time4 burn code fragment also.

Link to comment
Share on other sites

kOS v1.0.1 Let's take some input!

for KSP v1.1.3 Downloads this release

DOWNLOADS

Why 1.1.3 and not 1.2?

We wanted to get the last bug fixes and new features into the hands of any users
who might not update KSP to 1.2 right away. Traditionally there are some mods
that take a while to update when KSP releases a new version, and many users
choose to wait for all of their favorite mods to update before upgrading KSP.
By releasing in conjunction with the update, we can ensure that as many users as
possible have access to these latest updates. We will be releasing a version of
kOS that is compatible with KSP 1.2 as soon as possible after the final build is

BREAKING CHANGES

  • As always, if you use the compiler feature to make KSM files, you should recompile the KSM files when using a new release of kOS or results will be unpredictable.
  • The stage command/function now implements the yield behavior, waiting until the next frame to return. This ensures that all vessel stats are updated together. (#1807)

NEW FEATURES

  • Functions and opcodes can now tell the CPU to yield (wait) based on their own arbitrary logic. This allows future functions to be "blocking" (preventing further execution) without blocking KSP itself. (#1805, #1807, and #1820)
  • New timewarp structure, available on the kuniverse bound variable. This structure provides additional information and control over time warp. The old warp bound variables remain in place. (#1790 and #1820)
  • Introducing a new terminalinput structure for keyboard interaction from within scripts! Currently support is only provided for getting single characters. (#1830)

Please check http://ksp-kos.github.io/KOS_DOC/changes.html for more detailed
explanations for the new features.

BUG FIXES

  • Fix for formatting of time:clock to pad zeros (#1771 and #1772)
  • Fix for not being able to construct a vessel("foo") if "foo" is the name of the current vessel (#1565and #1802)
  • RemoteTech steering should be fixed. At worst you may see a 1sec gap with the controls, as we now refresh the steering callback about once per second. (#1806 and #1809)
  • Named functions defined within anonymous functions will no longer throw an error (#1801 and#1811)
  • lock steering no longer throws an exception inside of an anonymous functions (#1784 and #1811)
  • Compiled programs that include a large number of named functions should no longer throw an error (#1796 and #1812)
  • Fixed the first call to wait after the cpu boots (#1785)
  • Various documentation fixes (#1810, #1823, and #1834)
Edited by hvacengi
Fixed github link to point to current version
Link to comment
Share on other sites

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