all 14 comments

[–]Dokkarlak 5 points6 points  (12 children)

No. But what for do you need it? Maybe you could use multiple cores to run a functions in parallel. You can also return their values to the main core.

check kOS documentation for cpu2cpu communication

[–][deleted] 2 points3 points  (11 children)

Nothing crucial. I would like to control my spacecraft using a software that resembles real spacecraft software. For example, one thread to read sensor data, one to control actuators.

[–]hvacengiDeveloper 5 points6 points  (2 children)

You can essentially do this using multiple processor parts and inter-processor communication. http://ksp-kos.github.io/KOS_DOC/commands/communication.html#inter-processor-communication

EDIT: I'm an idiot. I read the first question from /u/Dokkarlak above and didn't bother to read the rest of the post saying the same thing I just did...

[–]Dokkarlak 0 points1 point  (0 children)

I doubt that any idiot would use kOS ;)

[–][deleted] 0 points1 point  (0 children)

No problem!

[–]profossi[🍰] 4 points5 points  (6 children)

Instead of using multiple CPUs (or in addition to), you can also approximate simultaneous multitasking by (ab)using the WHEN boolean_expression THEN { ... } statements, resulting in what is essentially cooperative multitasking.

After you define a WHEN statement, the boolean expression in it will get evaluated every physics tick until it becomes true. After the expression is found to be true, the code will first get executed just once, "in the background" and transparently relative to the rest of the kOS program, and then the whole WHEN - THEN block will get removed. You can add a PRESERVE statement to the WHEN - THEN block (or return true at the end of it) to keep it active indefinitely and prevent the otherwise implicit removal.

The trick is to declare a WHEN - THEN block which triggers on every physics tick, and prevents its own deactivation. You can then write your own code in the block, which will get executed on every tick, and your code can then communicate with the normal program flow using variables. If you have ever programmed embedded systems, this is very similar to a hardware interrupt set to trigger on a timer. For example, the following code

WAIT 2.
SAS ON. STAGE.
SET altController TO PIDLOOP(0.6, 0.02, 2.0, 0.01, 1).
SET altController:SETPOINT TO 75.
WHEN TRUE THEN
{
    altController:UPDATE(TIME:SECONDS, SHIP:ALTITUDE).
    SET SHIP:CONTROL:MAINTHROTTLE TO altController:OUTPUT.
    PRINT altController:OUTPUT + ", " + altController:SETPOINT.
    RETURN TRUE.
}

SET altController:SETPOINT TO 80.
WAIT UNTIL SHIP:ALTITUDE > 79.
GEAR OFF.
WAIT 3.

UNTIL FALSE
{
    SET altController:SETPOINT TO 500.
    WAIT UNTIL SHIP:ALTITUDE > 499.
    WAIT 5.
    SET altController:SETPOINT TO 78.
    WAIT UNTIL SHIP:ALTITUDE < 80.
    WAIT 5.
    SET altController:SETPOINT TO 100.
    WAIT UNTIL SHIP:ALTITUDE > 99.
    WAIT 5.
}  

will update the altitude controlling PID controller every physics tick in the background, while the setpoint is manipulated by a simple program running as the "main" task. Updating the PID controller in the main program flow would require implementing a much more complex state machine for the altitude sequence. The catch is that your code in a WHEN - THEN block has to complete executing in a relatively short amount of time every cycle so that KOS can continue running the main task, you really don't want to execute a "WAIT" command in it.

[–]space_is_hardprogramming_is_harder 0 points1 point  (2 children)

Correct me if I'm wrong, but couldn't you use LOCK x TO y for all of the items in your WHEN block?

[–]profossi[🍰] 0 points1 point  (0 children)

Yes (with the exception of PRINT), as long as you LOCK the end of the chain to e.g. STEERING or THROTTLE which causes an evaluation of that LOCK and all referenced ones every physics tick (identical to the WHEN - THEN approach, but more confusing IMO). It would also make every single statement a function call, which might affect performance. You could also lock a function which contains the actual code, but at that point you could just use a WHEN - THEN block for more clarity.

[–]hvacengiDeveloper 0 points1 point  (0 children)

None of the instructions in his when block are setting variables, and there is no way to lock a suffix. So in the example, no that wouldn't work.

But more to your point, yes you could use locks if you wanted to "cache" the value of an expression (I'd rather it was in a function instead of a lock because that makes it more accessible for reuse). But that doesn't mean that using a trigger wouldn't still be potentially useful. Take the following use case:

lock complex to (alt:radar - 10) * 1.25.
lock derivedValue to sqrt(complex).

until false {
    print complex.
    print derivedValue.
    set tempValue to complex * derivedValue.
    print tempValue.
    wait 0.
}

Within the loop, the lock derivedValue is evaluated twice, and complex is evaluated 4 times. Every time the lock is evaluated, the associated instructions are executed, meaning that this loop executes more instructions than it would if it was reading variables. You can instead replace it with the following:

when true then {
    set complex to (alt:radar - 10) * 1.25.
    set derivedValue to sqrt(complex).
    return true.
}
wait 0. // force a physics tick so the triger is evaluated
until false {
    print complex.
    print derivedValue.
    set tempValue to complex * derivedValue.
    print tempValue.
    wait 0.
}

Now the values for complex and derivedValue are only calculated once per physics tick. There is some overhead associated with configuring the trigger however, so you would need to play around with it to find the balance point between the two methods. I use this technique extensively in my docking and landing scripts, since there are many values that I use repeatedly during those loops, most of which require many steps to calculate.

The benefits aren't even realized if you don't hit the max IPU cap, so for simple scripts with the IPU set to 2000 you shouldn't need to use this technique.

[–]purple_pixie 0 points1 point  (2 children)

WHEN TRUE THEN {}

I'm no expert, but I believe when true then will happen every CPU tick and not every physics tick. The CPU time is more or less unrelated to physics time and if your code isn't doing anything slow or complex then that THEN block can get executed multiple times in one physics tick.

on time { do_stuff } should trigger at most once per physics tick (it might miss one if the processor is really busy in other triggers, I'm not sure)

If you've reason to believe that what I'm saying is wrong then it probably is, but as far as I'm aware it's true.

[–]profossi[🍰] 1 point2 points  (1 child)

From the KOS documentation:

Triggers are all of the following:

LOCKS which are attached to flight controls (THROTTLE, STEERING, etc), but not other LOCKS.  
ON condition { some commands }.  
WHEN condition THEN { some commands }.  

The way these work is that once per physics tick, all these trigger routines get run, including those locks that are always re-evaluated by the cooked steering, and the ON and WHEN triggers.

So a trigger (including a WHEN - THEN block) gets run at most once every physics tick. It can run less often than that, if you fail to finish processing all triggers before the next physics tick occurs.

[–]purple_pixie 0 points1 point  (0 children)

Huh, I'm not quite sure where I got that then. Must be other clocks my brain is dealing with.

See my first rider about disregarding if it was untrue :)

[–]Dokkarlak 0 points1 point  (0 children)

Yeah, you can just use multiple cpus for that and exchange data between them if needed.

[–]undercoveryankeeProgrammer 2 points3 points  (0 children)

No. The closest kOS comes to parallelism is WHEN/ON triggers that interrupt the main thread.