Jump to content

[1.3] kOS Scriptable Autopilot System v1.1.3.0


erendrake

Recommended Posts

When making a KS file that only declares a global, after running, it doesn't seem like it looks for the file in the windows computer. It looks like whatever you ran is saved in to memory.

Are you aware of the difference between local volume and archive volume? Only the archive will end up on disk. The local is stored inside the part itself and only lives as long as the vessel is still present.

Link to comment
Share on other sites

One of the next features on deck is inter-cpu communication, where a kos CPU has a message queue of prmitive objects (strings, numbers, etc) that other kos CPU's can insert into, so as long as you invent your own protocol for what you want those strings and numbers to actually mean, you can have one talk to another and feed it information.

if that does happen it would be cool if kerbals could have there own cpu when on EVA. that way if you don't want to walk back to your ship, then write a script to send it your gps location and have it fly to you.

maybe don't let it control walking but a scriptable jetpack would be great!

Link to comment
Share on other sites

So is there a way to modify a local file midway through execution of takeoff code? That seems to make the problem worse.

What is it that you're trying to accomplish? Is it something that is doable by typing in kos commands every once in a while, or do you need to feed it constantly like a joystick?

If it's the first choice I'd go with the telnet choice. You can probably find many telnet clients in github in your preferred language and rig it to a) connect to kos B) read serial port from atmel and C) issue commands to the kos through telnet. You'd probably pre write kos scripts that implement those commands.

That is a way to avoid messing around with ksp linking and docs.

I'd try to stay away from rewriting .ks files on the fly. Maybe it works but I'd never be comfortable with that. What if some part of the file is getting written while it's being read? You have to assume lots of things for it go smoothly.

Link to comment
Share on other sites

What is it that you're trying to accomplish? Is it something that is doable by typing in kos commands every once in a while, or do you need to feed it constantly like a joystick?

If it's the first choice I'd go with the telnet choice. You can probably find many telnet clients in github in your preferred language and rig it to a) connect to kos B) read serial port from atmel and C) issue commands to the kos through telnet. You'd probably pre write kos scripts that implement those commands.

That is a way to avoid messing around with ksp linking and docs.

I'd try to stay away from rewriting .ks files on the fly. Maybe it works but I'd never be comfortable with that. What if some part of the file is getting written while it's being read? You have to assume lots of things for it go smoothly.

Trying to make a simulator. It would be every once in a while.

I'll try getting Telnet to work with my Microcontroller. Hopefully that idea works.

Thanks for the help Qigon.

Link to comment
Share on other sites

So is there a way to modify a local file midway through execution of takeoff code? That seems to make the problem worse.

I don't understand what you're trying to do. If you're trying to make self-modifying code of a sort, then yes there is a problem because kOS assumes during the life of a program that subsequent RUNs of the same file don't need recompiles of that file. That's because if you run a file in a loop like so :


until false {
run foo.
}

It shouldn't have to recompile foo over and over and over, which would be very slow.

Instead it knows that during the current run, foo got compiled once already so it re-uses the already compiled one in memory.

It's only when you're all the way back to the interactive terminal again that it turns that behavior off and actually recompiles everything from scratch again.

Link to comment
Share on other sites

I don't understand what you're trying to do. If you're trying to make self-modifying code of a sort, then yes there is a problem because kOS assumes during the life of a program that subsequent RUNs of the same file don't need recompiles of that file. That's because if you run a file in a loop like so :


until false {
run foo.
}

It shouldn't have to recompile foo over and over and over, which would be very slow.

Instead it knows that during the current run, foo got compiled once already so it re-uses the already compiled one in memory.

It's only when you're all the way back to the interactive terminal again that it turns that behavior off and actually recompiles everything from scratch again.

I'm glad to know it works like this, it just feels like any other internal behavior is asking for trouble.

Link to comment
Share on other sites

May I offer a little contribution?

It's a little modification to ENGINE to make it handle multimode engines (RAPIER) properly (originally it was referencing the primary mode all the time).

Additional suffixes:

for all engines:

MULTIMODE - true for multimode engines, false otherwise (read only)

MODES - list of modes for multimode engine (2 strings), otherwise returns a list of 1 string "Single mode"

for multimode only

MODE - name of the current mode (same as in the right-click menu)

TOGGLEMODE - same as the event in right-click menu

PRIMARYMODE - true for primary, false for secondary (get/set) can use set to toggle to specific mode.

AUTOSWITCH - true for automatic, false for manual switching (get/set)

still there is something wrong with LIST ENGINES command in terminal (LIST ENGINES IN <something> works properly), but everything else seems to work properly for now (tested on 1.0.5 recompile).

Main changes are to kOS.Suffixed.Part.EngineValue

using kOS.Safe.Encapsulation;
using kOS.Safe.Encapsulation.Part;
using kOS.Safe.Encapsulation.Suffixes;
using System.Collections.Generic;
using kOS.Safe.Exceptions;

namespace kOS.Suffixed.Part
{
public class EngineValue : PartValue
{
private IModuleEngine engine
{get {
if ((!MultiMode)||(MMengine.runningPrimary)) { return engine1; }
else { return engine2; }
}
}

private readonly IModuleEngine engine1;
private readonly IModuleEngine engine2;
private readonly MultiModeEngine MMengine; //multimodeengine module (null if not multimode)
private readonly bool MultiMode;

public EngineValue(global::Part part, IModuleEngine engine, SharedObjects sharedObj)
: base(part, sharedObj)
{
this.MMengine = null;
this.engine1 = engine;
EngineInitializeSuffixes();
MultiMode = false;
}

public EngineValue(global::Part part, MultiModeEngine engine, SharedObjects sharedObj)
: base(part, sharedObj)
{
this.MMengine = engine;

foreach (PartModule module in this.Part.Modules)
{
var engines = module as ModuleEngines;
if ((engines != null) && (engines.engineID == this.MMengine.primaryEngineID))
{
this.engine1 = new ModuleEngineAdapter(engines);
}
if ((engines != null) && (engines.engineID == this.MMengine.secondaryEngineID))
{
this.engine2 = new ModuleEngineAdapter(engines);
}
var enginesFX = module as ModuleEnginesFX;
if ((enginesFX != null) && (enginesFX.engineID == this.MMengine.primaryEngineID))
{
this.engine1 = new ModuleEngineAdapter(enginesFX);
}
if ((enginesFX != null) && (enginesFX.engineID == this.MMengine.secondaryEngineID))
{
this.engine2 = new ModuleEngineAdapter(enginesFX);
}

}
// throw exception if not found
if (engine1 == null) { throw new KOSException("Engine module error " + MMengine.primaryEngineID); }
if (engine2 == null) { throw new KOSException("Engine module error " + MMengine.secondaryEngineID); }
MultiMode = true;

EngineInitializeSuffixes();
}



private void EngineInitializeSuffixes()
{
AddSuffix("ACTIVATE", new NoArgsSuffix(() => engine.Activate()));
AddSuffix("SHUTDOWN", new NoArgsSuffix(() => engine.Shutdown()));
AddSuffix("THRUSTLIMIT", new ClampSetSuffix<float>(() => engine.ThrustPercentage, value => engine.ThrustPercentage = value, 0, 100, 0.5f));
AddSuffix("MAXTHRUST", new Suffix<float>(() => engine.MaxThrust));
AddSuffix("THRUST", new Suffix<float>(() => engine.FinalThrust));
AddSuffix("FUELFLOW", new Suffix<float>(() => engine.FuelFlow));
AddSuffix("ISP", new Suffix<float>(() => engine.SpecificImpulse));
AddSuffix(new[] { "VISP", "VACUUMISP" }, new Suffix<float>(() => engine.VacuumSpecificImpluse));
AddSuffix(new[] { "SLISP", "SEALEVELISP" }, new Suffix<float>(() => engine.SeaLevelSpecificImpulse));
AddSuffix("FLAMEOUT", new Suffix<bool>(() => engine.Flameout));
AddSuffix("IGNITION", new Suffix<bool>(() => engine.Ignition));
AddSuffix("ALLOWRESTART", new Suffix<bool>(() => engine.AllowRestart));
AddSuffix("ALLOWSHUTDOWN", new Suffix<bool>(() => engine.AllowShutdown));
AddSuffix("THROTTLELOCK", new Suffix<bool>(() => engine.ThrottleLock));
AddSuffix("ISPAT", new OneArgsSuffix<float, double>(GetIspAtAtm));
AddSuffix("MAXTHRUSTAT", new OneArgsSuffix<float, double>(GetMaxThrustAtAtm));
AddSuffix("AVAILABLETHRUST", new Suffix<float>(() => engine.AvailableThrust));
AddSuffix("AVAILABLETHRUSTAT", new OneArgsSuffix<float, double>(GetAvailableThrustAtAtm));

AddSuffix("MULTIMODE", new Suffix<bool>(() => MultiMode));
AddSuffix("MODES", new Suffix<ListValue>(GetAllModes, "A List of all modes of this engine"));


if (this.MultiMode)
{
AddSuffix("MODE", new Suffix<string>(() => MMengine.mode));
AddSuffix("TOGGLEMODE", new NoArgsSuffix(() => ToggleMode() ));
AddSuffix("PRIMARYMODE", new SetSuffix<bool>(() => MMengine.runningPrimary, value => ToggleSetMode(value)));
AddSuffix("AUTOSWITCH", new SetSuffix<bool>(() => MMengine.autoSwitch, value => SetAutoswitch(value)));



}



}

public static ListValue PartsToList(IEnumerable<global::Part> parts, SharedObjects sharedObj)
{
var toReturn = new ListValue();
foreach (var part in parts)
{
bool ismultimode = false;
foreach (PartModule module in part.Modules)
{
var enginesMM = module as MultiModeEngine;

if (enginesMM != null)
{
toReturn.Add(new EngineValue(part, enginesMM, sharedObj));
ismultimode = true;
}
}
if (!ismultimode) {
foreach (PartModule module in part.Modules)
{
var engines = module as ModuleEngines;
if (engines != null)
{
toReturn.Add(new EngineValue(part, new ModuleEngineAdapter(engines), sharedObj));
}
else
{
var enginesFX = module as ModuleEnginesFX;
if (engines != null)
{
toReturn.Add(new EngineValue(part, new ModuleEngineAdapter(enginesFX), sharedObj));
}
}
}
}
}
return toReturn;
}

public float GetIspAtAtm(double atmPressure)
{

return engine.IspAtAtm(atmPressure);
}

public float GetMaxThrustAtAtm(double atmPressure)
{
return engine.MaxThrustAtAtm(atmPressure);
}

public float GetAvailableThrustAtAtm(double atmPressure)
{
return engine.AvailableThrustAtAtm(atmPressure);
}

public ListValue GetAllModes()
{
var toReturn = new ListValue();
if (this.MultiMode) { toReturn.Add(MMengine.primaryEngineID); toReturn.Add(MMengine.secondaryEngineID); }
else { toReturn.Add("Single mode"); }

return toReturn;
}

public void ToggleMode()
{
MMengine.Invoke("ModeEvent", 0);
}

public void ToggleSetMode(bool prim)
{
if (prim != MMengine.runningPrimary) { ToggleMode(); }
}

public void SetAutoswitch(bool auto)
{
if (auto) { MMengine.Invoke("EnableAutoSwitch", 0); }
else { MMengine.Invoke("DisableAutoSwitch", 0); }
}

}
}

Also it required some update to kOS.Suffixed.Part.PartValueFactory

using System.Collections.Generic;
using System.Linq;
using kOS.Safe.Encapsulation;

namespace kOS.Suffixed.Part
{
public class PartValueFactory
{
public static ListValue Construct(IEnumerable<global::Part> parts, SharedObjects shared)
{
var partList = parts.Select(part => Construct(part, shared)).ToList();
return ListValue.CreateList(partList);
}

public static ListValue<PartValue> ConstructGeneric(IEnumerable<global::Part> parts, SharedObjects shared)
{
var partList = parts.Select(part => Construct(part, shared)).ToList();
return ListValue<PartValue>.CreateList(partList);
}

public static PartValue Construct(global::Part part, SharedObjects shared)
{
foreach (PartModule module in part.Modules) //Check for mutimode engine (let's not run into usual engine modules first)
{
MultiModeEngine mmEng = module as MultiModeEngine;
if (mmEng != null)
return new EngineValue(part, mmEng, shared);
}
foreach (PartModule module in part.Modules)
{
ModuleEngines mEng = module as ModuleEngines;
if (mEng != null)
return new EngineValue(part, new ModuleEngineAdapter(mEng), shared);
ModuleEnginesFX mEngFX = module as ModuleEnginesFX;
if (mEngFX != null)
return new EngineValue(part, new ModuleEngineAdapter(mEngFX), shared);
ModuleDockingNode mDock = module as ModuleDockingNode;
if (mDock != null)
return new DockingPortValue(mDock, shared);
ModuleEnviroSensor mSense = module as ModuleEnviroSensor;
if (mSense != null)
return new SensorValue(part, mSense, shared);
var gimbalModule = module as ModuleGimbal;
if (gimbalModule != null)
return new GimbalValue(gimbalModule,shared);

}

// Fallback if none of the above: then just a normal part:
return new PartValue(part, shared);
}
}
}

Yeah, it started with recompiling kOS for 1.0.5 due to a broken doaction call I used to toggle Rapiers on my craft, but ended with reverse engineering and rewriting kOS engine module to support Rapiers normally. Too much engineering approach.

Edited by Alchemist
Link to comment
Share on other sites

Hey there, just discovering kOS... and learning this programming stuff...

trying to find out, what I might command directly, not using an AG.

what I have not found till now, while taking a quick look around, is how to activate antennas/dishes...

Link to comment
Share on other sites

what I have not found till now, while taking a quick look around, is how to activate antennas/dishes...

If you just want to deploy antennas (like with right-click menus) or control other such things that are not specifically recognized by kOS, you'll have to find the PartModule and use its doEvent or doAction calls. Of course, getting the proper module and the name of the event is a complicated question.

So I'd recommend first reading PartModule documentation. And looking at the part.cfg and the part's right-click menu for clues.

Link to comment
Share on other sites

May I offer a little contribution?

It's a little modification to ENGINE to make it handle multimode engines (RAPIER) properly (originally it was referencing the primary mode all the time).

Do you have an account on github, or would you be willing to make one? We make heavy use of it for organizing code coming from multiple contributors, and this sort of thing is best handled there.

Link to comment
Share on other sites

This is really wonderful mod (except not very convenient language syntax :), but I found it really slow on calculations - I'm trying to implement calculation for suicide burn through numerical integration (integral seems to be too complex for analytical approach), and it takes about 3-4 second for ~300 iterations of pretty simple calculations - I've made some tests and seems just every couple of math operations takes ~1ms. Is there any way to optimize it somehow?

Link to comment
Share on other sites

Do you have an account on github, or would you be willing to make one? We make heavy use of it for organizing code coming from multiple contributors, and this sort of thing is best handled there.

Ok, got there, I think. No experience with such things, so sorry if I mess something up.

Also added GIMBAL into ENGINE, because why else it is there for.

Link to comment
Share on other sites

This is really wonderful mod (except not very convenient language syntax :), but I found it really slow on calculations - I'm trying to implement calculation for suicide burn through numerical integration (integral seems to be too complex for analytical approach), and it takes about 3-4 second for ~300 iterations of pretty simple calculations - I've made some tests and seems just every couple of math operations takes ~1ms. Is there any way to optimize it somehow?

The default CPU speed is deliberately throttled back severely for two reasons:

1 - We don't want to take up more than our fair share of the simulation time, so we want to make sure kOS only uses a very small fraction of the total time, leaving lots of CPU (real CPU) time available for other computationally-hungry things like the stock patch solver, the FAR calculations, etc. (And we are simulating old era slow computing here anyway).

2 - The simulated virtual computer considers all "instructions" to have the same weight even though in reality they don't and some take a lot longer than others to execute. In order to prevent you from trying to run, say SET FOO TO SHIP:PARTS, which requires a full tree walk of the parts tree, tens of thousands of times per second, it also ends up preventing you from running a much less expensive operation like SET X TO X + 1 tens of thousands of times per second. In other words it's severely throttled back because it's treating every instruction like the worst case scenario instruction, and we picked a setting pretty much guaranteed to have a rather small runtime impact even on a slow gaming rig running inefficient scripts.

If you think the default choice is too slow, and you want to experiment with how it behaves with it set higher, try this:


set CONFIG:IPU to nnnn.

Where nnnn is some number in the range of 50 to 5000. The default we ship with is 200, but many people have reported that they can increase it to 10 or 20 times that much on their rig and still get acceptable performance and frame rate. This setting, once changed, is global and stays the same for the entire mod (across all saved games). I'd like some day to experiment with implementing differently speeded CPUS on the tech tree - slow ones early, fast ones later, but right now it's just a dumb single global setting.

You can try to change this setting on the fly in your script, if you're being adventurous. (For example, turning it up just before you enter a section where you know the script will be doing nothing but math in its head, and then turning it back down when you exit that section - just remember it keeps using the previous value until the end of the current physics tick, so you might want to force a WAIT 0.0001 just after any such change to ensure it really takes effect right away.)

Edited by Steven Mading
I hate having to disable the damned auto-smilies.
Link to comment
Share on other sites

Hey, I have a question about the new cooked steering... Is it possible to lock only certain axes? I want to implement a gravity turn logic: turn to 85 degrees above the horizon East at 50m/s, and release yaw.

Of course I can just release steering as it is, but for greater predictability, I guess it makes sense to keep roll and pitch locked, that way we're certain to end up in a nice equatorial orbit...
Link to comment
Share on other sites

[quote name='Steven Mading']The default CPU speed is deliberately throttled back severely for two reasons:[/QUOTE]
I have an idea for such kind of problems - it would be nice to add some kind of "hardware calculation modules" or coprocessors to kOS CPU. Such modules can be programmed separately in C#/C++ or other language and compiled into shared library and consist of functions. Every such module can be added to kOS CPU at the expense of price+weight+power consumption, providing fast (maybe even parallel) calculations.
Link to comment
Share on other sites

Is it possible to write a script for a probe that would sense the current battery level, then switch a fuel cell on based on that level?

I'd like to have the fuel cell kick on when the battery gets to 10%, then switch off when the battery gets to 90%.
Link to comment
Share on other sites

[quote name='Torgo']Is it possible to write a script for a probe that would sense the current battery level, then switch a fuel cell on based on that level?

I'd like to have the fuel cell kick on when the battery gets to 10%, then switch off when the battery gets to 90%.[/QUOTE]

[CODE]
list RESOURCES in res.
local capacity is 0.
local amount is 0.
for r in res {
if r:NAME = "ElectricCharge" {
lock capacity to r:CAPACITY.
lock amount to r:AMOUNT.
break.
}
}


local isON is false.
until false {
local p is 0.
if capacity > 0 {
set p to amount/capacity.
}
if p <= 0.1 and not isON {
list PARTS in pp.
for part in pp {
if part:NAME = "FuelCell" or part:NAME = "FuelCellArray" {
local module is part:GETMODULE("ModuleResourceConverter").
if module:HASEVENT("start fuel cell") {
module:DOEVENT("start fuel cell").
}
}
}
set isON to true.
} else if p >= 0.9 and isON {
list PARTS in pp.
for part in pp {
if part:NAME = "FuelCell" or part:NAME = "FuelCellArray" {
local module is part:GETMODULE("ModuleResourceConverter").
if module:HASEVENT("stop fuel cell") {
module:DOEVENT("stop fuel cell").
}
}
}
set isON to false.
}
wait 5.
}
[/CODE]

you can optimize it by taking parts' listing out of until loop Edited by z08840
Link to comment
Share on other sites

hi guys.

I'm having problem with some ir structure,

whenever I type:
print addons:ir:groups[0]:speed. at the terminal
it returns: "cannot cast from source type to destination type."

or when i try to set the speed it returns: "failed to convert parameters"

and I dont know what these messages means, am I doing something wrong?
Link to comment
Share on other sites

[quote name='Ricardo.b']hi guys.

I'm having problem with some ir structure,

whenever I type:
print addons:ir:groups[0]:speed. at the terminal
it returns: "cannot cast from source type to destination type."

or when i try to set the speed it returns: "failed to convert parameters"

and I dont know what these messages means, am I doing something wrong?[/QUOTE]
Can you give a dump of the output_log.txt ? It should have a much more verbose description of the error.
Link to comment
Share on other sites

[quote name='Steven Mading']Can you give a dump of the output_log.txt ? It should have a much more verbose description of the error.[/QUOTE]

I searched log.txt on ksp folder and it returned only changelogs from mods, where can I find this?

I'm using ubuntu linux
Link to comment
Share on other sites

[quote name='Ricardo.b']I searched log.txt on ksp folder and it returned only changelogs from mods, where can I find this?

I'm using ubuntu linux[/QUOTE]

For some idiotic reason, Unity decided to make the log output filenames differ per platform even though there's NO TECHNICAL OS-BASED REASON they have to do that, and the entire goal of Unity was to be a cross-platform engine, thus the name "unity".

On Linux, they put the log file here:

"/home/user/.config/unity3d/Squad/Kerbal Space Program/Player.log"

(Change /home/user to whatever your home dir is).

Disclaimer: I don't have the game on Linux so I can't verify this - this is from posts I've seen from others.
Link to comment
Share on other sites

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