Jump to content

The Kerbal KAL Logic & Computing Laboratory [WIP]


Nazalassa

Recommended Posts

The Kerbal KAL Logic & Computing Laboratory

thumbnail

KerbalX hangar is here, git repo is here

IMPORTANT NOTE: It is normal this project is going slowly. We're still working on it, but not very fast.

 +-----+
 | LOG |
 +-----+

 [ 230302.1751 ] Created thread.
 [ 230303.1542 ] Experiment 1: analog addition attempt (see log 23.1 and analysis 23.2)
 [ 230304.0937 ] Working bus (see log 23.3)
 [ 230305.1452 ] Working adder (see log 23.4)
 [ 230308.1649 ] Working instruction KALs (see log 23.5)
 [ 230319.1713 ] Thread got accidentally moved (see incident 23.6)
 [ 230330.1446 ] Experiment 2: KAL curves outside time range [0,1] attempt (see log 23.7)
 [ 230422.1602 ] Experiment 3: "Tape" memory idea + .craft file AG modification attempt (see log 23.8)
 [ 230506.1621 ] CFG File Parser (CFP) finished.
 [ 230714.1055 ] Bus with MOV instruction experiment (see log 23.9)
 [ 240118.1815 ] CFP 0.3 finished: now usable and finally good. (see log 24.1)
 [ 240121.1126 ] 256 register bank wiring finished, now all that's left is to implement the MOV instruction. (see log 24.2)
 +----------------+
 | HARDWARE PACKS |
 +----------------+
 Usable (and generaly more user-friendly) sets of hardware, packed together in a single contraption, like computers
 
 [ 256 Analog Early Computer Prototype ] An adder + registers pack, which also has instruction KALs you can run (TAB, ADD, etc.). Still not a computer. [I may redo this description]
 []
 +-------------------------+
 | BINARY HARDWARE LIBRARY |
 +-------------------------+
 Hardware that operates on a single bit per KAL
 
 [ 1-bit AND Gate ] 1-bit AND Gate.
 [ 1-bit AND Gate Simplified ] Simplified version of the above 1-bit AND gate.
 [ 6-bit Adder ] A simple binary adder that adds two 6-bit numbers. Why 6-bit? Because there was only room for 6 KALs.
 []
 +-----------------------------+
 | I/O BINARY HARDWARE LIBRARY |
 +-----------------------------+
 Hardware that handle I/O (Input/Output) with one bit per KAL
 
 [ 8-bit Triple 7-Segment Display ] Displays an 8-bit number.
 []
 +-------------------------+
 | ANALOG HARDWARE LIBRARY |
 +-------------------------+
 Hardware that operate on KAL play positions, to represents larger numbers in a single KAL
 
 [ 256 Bus with 3 Registers ] 256 analog bus prototype, with 3 analog registers.
 [ 256 Analog Adder ] Adds two 256 analog numbers. It has a carry in and a carry out, allowing it to be stacked.
 [ Analog Subtraction ] Note: may require some investigations (to determine range etc.)
 [ 256 Analog Bus (3 Registers) + Adder ] 256 analog bus prototype, with 3 analog registers. It has an adder that can add two 256 analog numbers (A+B → A).
 [ MOV-256 Bus ] 256 analog bus prototype, featuring a MOV instruction. It can therefore have up to 256 registers.
 [ MOV-BUS-256 Register Pack ] A pack of 256 registers, with a working bus and MOV instruction. 
 +-----------------------------+
 | I/O ANALOG HARDWARE LIBRARY |
 +-----------------------------+
 Hardware that handle analog I/O (Input/Output) with KAL play positions
 
 [ 256 Triple 7-Segment Display ] Displays a 256 analog number.
 [ Smoothed State Follower ] Note: may require some investigations (to determine range etc.)
 []
 +------------------------+
 | OTHER HARDWARE LIBRARY |
 +------------------------+
 Hardware that convert between binary and analog, etc.
 
 [ 8-bit 256 Binary to Analog Converter ] Converts an 8-bit binary number into a 256 analog number.
 [ 256 8-bit Analog to Binary Converter ] (Not made yet, but that's a logical thing to do)
 []

 

 +------------------+
 | PYTHON UTILITIES |
 +------------------+
 Python scripts that help using KALs, such as track generators, etc.

 [ CFP ] A craft file editor, which supports scripts. Comes with KAL-Utils script suite.
 [ Old CFG File Parser (CFP) ] A graphical program that uses Tk, to view and edit .craft files (and KSP cfgs in general).
 [ QnD Generate KAL Curve ] A quick-and-dirty python script that takes a list of values, and makes them a KAL curve. Can be used with CFP.
 []
 

 

For those who want to study KALs without having access to KSP, here's an extract of a .craft file, showing a KAL:

Spoiler
PART
{
	part = controller1000_4294451462
	partName = Part
	persistentId = 2563744445
	pos = 0.663985729,10.0025492,-0.706833422
	attPos = 0.699999988,-0.0166683197,-1.34075487
	attPos0 = 0,0.0166683197,0.640754819
	rot = 0,0.707106709,-0.707106829,0
	attRot = 0,0,0,1
	attRot0 = 0,0.707106709,-0.707106829,0
	mir = 1,1,1
	symMethod = Mirror
	autostrutMode = Off
	rigidAttachment = False
	istg = -1
	resPri = 0
	dstg = 0
	sidx = -1
	sqor = -1
	sepI = -1
	attm = 1
	sameVesselCollision = False
	modCost = 0
	modMass = 0
	modSize = 0,0,0
	srfN = srfAttach,structuralPanel2_4294458818,panel,0|0|0,0|0|-1,0|0|0
	EVENTS
	{
	}
	ACTIONS
	{
		ToggleSameVesselInteraction
		{
			actionGroup = None
			wasActiveBeforePartWasAdjusted = False
		}
		SetSameVesselInteraction
		{
			actionGroup = None
			wasActiveBeforePartWasAdjusted = False
		}
		RemoveSameVesselInteraction
		{
			actionGroup = None
			wasActiveBeforePartWasAdjusted = False
		}
	}
	PARTDATA
	{
	}
	MODULE
	{
		name = ModuleRoboticController
		isEnabled = True
		persistentId = 2591903450
		displayName = INPUT A5
		sequencePosition = 10
		sequencePlaySpeed = 0
		sequenceLength = 10
		controllerEnabled = True
		priorityField = 1
		windowPosition = (246, -392)
		windowSize = (618, 419)
		stagingEnabled = True
		sequenceIsPlaying = False
		sequenceDirection = Forward
		sequenceLoopMode = Repeat
		EVENTS
		{
		}
		ACTIONS
		{
			TogglePlayAction
			{
				actionGroup = Custom10
				wasActiveBeforePartWasAdjusted = False
			}
			ToggleLoopModeAction
			{
				actionGroup = None
				wasActiveBeforePartWasAdjusted = False
			}
			ToggleDirectionAction
			{
				actionGroup = None
				wasActiveBeforePartWasAdjusted = False
			}
			ToggleControllerEnabledAction
			{
				actionGroup = None
				wasActiveBeforePartWasAdjusted = False
			}
			ToggleControllerEnabledOn
			{
				actionGroup = None
				wasActiveBeforePartWasAdjusted = False
			}
			ToggleControllerEnabledOff
			{
				actionGroup = None
				wasActiveBeforePartWasAdjusted = False
			}
			PlaySequenceAction
			{
				actionGroup = None
				wasActiveBeforePartWasAdjusted = False
			}
			StopSequenceAction
			{
				actionGroup = None
				wasActiveBeforePartWasAdjusted = False
			}
			SequenceForwardAction
			{
				actionGroup = None
				wasActiveBeforePartWasAdjusted = False
			}
			SequenceReverseAction
			{
				actionGroup = None
				wasActiveBeforePartWasAdjusted = False
			}
			SequenceLoopOnceAction
			{
				actionGroup = None
				wasActiveBeforePartWasAdjusted = False
			}
			SequenceLoopRepeatAction
			{
				actionGroup = None
				wasActiveBeforePartWasAdjusted = False
			}
			SequenceLoopPingPongAction
			{
				actionGroup = None
				wasActiveBeforePartWasAdjusted = False
			}
			SequenceLoopOnceRestartAction
			{
				actionGroup = None
				wasActiveBeforePartWasAdjusted = False
			}
			SequencePlaySpeedZeroAction
			{
				actionGroup = None
				wasActiveBeforePartWasAdjusted = False
			}
			SequencePlaySpeedFullAction
			{
				actionGroup = None
				wasActiveBeforePartWasAdjusted = False
			}
		}
		AXISGROUPS
		{
			sequencePosition
			{
				axisGroup = None
				axisIncremental = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
				axisSpeedMultiplier = 0
				axisInverted = None
				overrideIncremental0 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
				overrideIncremental1 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
				overrideIncremental2 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
				overrideIncremental3 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
			}
			sequencePlaySpeed
			{
				axisGroup = None
				axisIncremental = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
				axisSpeedMultiplier = 0
				axisInverted = None
				overrideIncremental0 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
				overrideIncremental1 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
				overrideIncremental2 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
				overrideIncremental3 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
			}
		}
		CONTROLLEDAXES
		{
			AXIS
			{
				persistentId = 210973617
				moduleId = 4023821551
				partNickName = Spotlight Mk1
				rowIndex = 0
				axisName = lightR
				timeValueCurve
				{
					key = 0 0 0 0
					key = 0.498 0 0 0
					key = 0.5 1 0 0
					key = 1 1 0 0
				}
			}
			AXIS
			{
				persistentId = 210973617
				moduleId = 4023821551
				partNickName = Spotlight Mk1
				rowIndex = 1
				axisName = lightG
				timeValueCurve
				{
					key = 0 0 0 0
					key = 0.498 0 0 0
					key = 0.5 1 0 0
					key = 1 1 0 0
				}
			}
			AXIS
			{
				persistentId = 210973617
				moduleId = 4023821551
				partNickName = Spotlight Mk1
				rowIndex = 2
				axisName = lightB
				timeValueCurve
				{
					key = 0 0 0 0
					key = 0.498 0 0 0
					key = 0.5 1 0 0
					key = 1 1 0 0
				}
			}
			AXIS
			{
				persistentId = 3915516560
				moduleId = 3186489012
				partNickName = KAL-1000 Controller
				rowIndex = 3
				axisName = sequencePosition
				timeValueCurve
				{
					key = 0 0 0 0
					key = 0.5 0 0 0
					key = 0.51 1 0 0
					key = 1 1 0 0
				}
			}
		}
		CONTROLLEDACTIONS
		{
		}
		UPGRADESAPPLIED
		{
		}
	}
	MODULE
	{
		name = ModuleCargoPart
		isEnabled = True
		beingAttached = False
		beingSettled = False
		reinitResourcesOnStoreInVessel = False
		stagingEnabled = True
		EVENTS
		{
		}
		ACTIONS
		{
		}
		UPGRADESAPPLIED
		{
		}
	}
}

 

Edited by Nazalassa
Moved image to Codeberg and added link to git repo
Link to comment
Share on other sites

Analog calculations will be fast and very KAL efficient, but as with real analog computers compounding errors will become a problem. This will be an issue for OP Codes and precise integer ops, but should be fine for most general purpose calculations. After all, this only runs on floats, and well floats even outside of KALs have their limits on precision. For OP Codes though we will need to have a stairstepped KAL to rule out any reading errors, cause if you're .1 off it doesn't matter much, but if you do ADD instead of POP STACK that will become a major issue. 

Link to comment
Share on other sites

Log 23.1

 

Analog logic gets too imprecise if the required precision (in this case 1/256, to represent 8-bit numbers) gets too small. All in the picture below:

6mbg90X.png

I entered at least 8 significant digits for all coordinates of all points in all curves, I made the curve between points segments (using the editor function), and the precision is like uuuh...
If operations need to be chained, this will be an issue. A big issue. In the example I gave, chaining two additions will give wrong results. This is the fault of KSP's lack of precision on plenty of things. Don't worry, I double-checked my math.

So yes it appears that we can store ROM as play positions, but operations, unless we want hundreds or even thousands of points, will likely be bitwise. Of course I can use the python script to give a specific value to each integer play position, but in this case the .craft will be veeeeery long, and even if I don't mind pasting 32768 lines in a .craft, we should find other ideas.

Edited by Nazalassa
Moved Experiment 1 log here
Link to comment
Share on other sites

Analysis 23.2

 

On 3/3/2023 at 5:31 PM, HB Stratos said:

Analog calculations will be fast and very KAL efficient, but as with real analog computers compounding errors will become a problem. This will be an issue for OP Codes and precise integer ops, but should be fine for most general purpose calculations. After all, this only runs on floats, and well floats even outside of KALs have their limits on precision. For OP Codes though we will need to have a stairstepped KAL to rule out any reading errors, cause if you're .1 off it doesn't matter much, but if you do ADD instead of POP STACK that will become a major issue. 

I think it would be fine if we made like 8-bit blocks (between 0 and 255) and stick two of them together when we need 16 bits etc., and hardcode very precise values with the help of the python script. As the script generates things like that:

+---------------------+
|                 ___ |
|           _____|    |
|     _____|          |
| ___|                |
+-0-----1-----2-----3-+

So if there's a tiny, tiny imprecision, it will be corrected.

After analysing the way the analog adder I made works, I think that the issue is that I made something like tha instead:

+------------------+
|             _,-' |
|         _,-'     |
|     _,-'         |
| _,-'             |
+-0----1----2----3-+

(Imagine the line is a straight line between start and stop)
So the source of the imprecision is probably just the fact that I was lazy and didn't literally hardcode the values in the "flat" way I showed above (the first one). I'll try to use the first way and see if it works.

On 3/3/2023 at 5:31 PM, HB Stratos said:

Analog calculations will be fast and very KAL efficient,

Also I noticed bitwise addition is like 1 physics tick or something, the lower that we can get.

Edited by Nazalassa
Moved Experiment 1 analysis here
Link to comment
Share on other sites

OK I just copy-pasted around 2,000 lines of timestamps in the .craft. Time to see how it behaves.

(cross your fingers :P)

--

Oh great, 0 parts in 1 stage. I guess KSP doesn't like it.

We have to find another way, I'll post the script in the OP rn (doc will follow)

Edited by Nazalassa
Link to comment
Share on other sites

Am I to understand you are looking at using the KAL as a l low level computer instead of a high level controller?  If not what am I missing?  Interesting exercise. 

Inspired by logic design courses while in college I made a fixed point basic op library for the Amiga 1000 blitter chip that could do add, subtract, and multiply on huge arrays of fixed point numbers (the blitter is for doing logical bit ops on entire bitplanes, mostly used for graphics).  I got stumped on a division op that had decent precision and finals were coming up so let it fade away from my to-do list.  Yes, years ago before FPUs and mega graphics cards were super common

Edited by darthgently
Link to comment
Share on other sites

4 hours ago, darthgently said:

Am I to understand you are looking at using the KAL as a l low level computer instead of a high level controller?  If not what am I missing?  Interesting exercise. 

Inspired by logic design courses while in college I made a fixed point basic op library for the Amiga 1000 blitter chip that could do add, subtract, and multiply on huge arrays of fixed point numbers (the blitter is for doing logical bit ops on entire bitplanes, mostly used for graphics).  I got stumped on a division op that had decent precision and finals were coming up so let it fade away from my to-do list.  Yes, years ago before FPUs and mega graphics cards were super common

Yes, we are looking at making a computer out of KALs. if you want to look at the basic writeups I did of the underlying things we can do with KALs, look here: https://kerbalx.com/HB_Stratos/Analog-Subtraction , https://kerbalx.com/HB_Stratos/Smoothed-State-Follower

 

also @Nazalassa I am currently writing a parser for .craft files in python that will output nested dictionaries similar what the python json.loads() does. Might be of use to combine with your script once it's done, then you don't have to copy-paste into craft files anymore.

Link to comment
Share on other sites

15 minutes ago, HB Stratos said:

Yes, we are looking at making a computer out of KALs. if you want to look at the basic writeups I did of the underlying things we can do with KALs, look here: https://kerbalx.com/HB_Stratos/Analog-Subtraction , https://kerbalx.com/HB_Stratos/Smoothed-State-Follower

 

also @Nazalassa I am currently writing a parser for .craft files in python that will output nested dictionaries similar what the python json.loads() does. Might be of use to combine with your script once it's done, then you don't have to copy-paste into craft files anymore.

That is very cool.  I've done smooth control of parts with kOS but overall have stopped using robotic parts as DockRotate + PicoPorts for joints works for most of what I want to do.   But yours will work without mods from what I can tell

Link to comment
Share on other sites

12 hours ago, HB Stratos said:

also @Nazalassa I am currently writing a parser for .craft files in python that will output nested dictionaries similar what the python json.loads() does. Might be of use to combine with your script once it's done, then you don't have to copy-paste into craft files anymore.

Nice! Then if we couple both scripts it'll be easier :)

I think I have an idea for a 8-bit analog bus with 8-bit analog registers, it would use 1 KAL for the bus itself and 3 KALs per register.

 

17 hours ago, darthgently said:

Am I to understand you are looking at using the KAL as a l low level computer instead of a high level controller?  If not what am I missing?  Interesting exercise.

The main reason we do that is probably because it's fun and we may will learn things about how computers work.

Link to comment
Share on other sites

11 hours ago, darthgently said:

That is very cool.  I've done smooth control of parts with kOS but overall have stopped using robotic parts as DockRotate + PicoPorts for joints works for most of what I want to do.   But yours will work without mods from what I can tell

Yeah that's the plan.. sadly Sensors (except for a single use collision sensor) are outside the range of possibility for now, so a PID controller would not be possible not because it can't be coded with KALs, but because we don't have any real world data we can use as input 

Link to comment
Share on other sites

On 3/4/2023 at 11:33 AM, HB Stratos said:

Yeah that's the plan.. sadly Sensors (except for a single use collision sensor) are outside the range of possibility for now, so a PID controller would not be possible not because it can't be coded with KALs, but because we don't have any real world data we can use as input 

It would be totally possible to detect staging (at least when something decouples) or keyboard inputs that trigger either a specific action group, or pitch yaw that kind of things.

 

-- [ MERGED WITH BELOW POST ] --

Log 23.3

 

OK the bus works! yeeeey &)

KALlogic_025.png

Original state (I changed the values of the registers myself)
 

KALlogic_026.png

Register A writes to the bus (AR A WRITE is enabled) and register B loads from the bus (AR B READ is enabled), effectively doing an A to B transfer.
 

KALlogic_027.png

Now register C writes to the bus and register B reads, transfering data from C to B.
 

KALlogic_028.png

Here's how it works! Each of the three lines is a register. Each register has 3 KALs, from left to right: read, store, write. Normally read and write are disabled, although store is always enabled. When write is enabled, it writes the contents of the register to the bus. When read is enabled, it stores the contents of the bus into the register.

The thing works like that: Basically each KAL is a buffer, the bus writes into the read KALs, which each write into their register's store KAL, which does the same with the write KAL, which writes to the bus again. It's like this:

R → S → W  ]-- Register
  ↖   ↙
   Bus     ]-- Bus

However, the read and write KALs of each register aren't always enabled, which means that we can decide when we want eachregister to read/write from/to the bus.
 

Oh, by the way, here's how it looks in one KAL:

KALlogic_024.png

Instead of having the sloped stuff from the adder, I used my script to generate a buffer, which basically sets the play position of the target KAL to the one of the current one.

Download: https://kerbalx.com/Nazalassa/8-bit-bus-prototype

Edited by Nazalassa
Images to web.archive.org
Link to comment
Share on other sites

[ WARNING | This post has been repurposed. It is roughly one day younger. ]


I modified the OP and added a library to keep track of all the KAL logic & computing stuff we do :)
Right now there are the crafts from @HB Stratos and me.

 

If we want to make a CPU I suggest we first try to make an all-8-bit one, 256 bytes of total memory (maybe 192 program ROM, the rest is I/O, RAM, and possibly stack), with a small set of operations (LDA, LDB, STA, STB, LAV, LBV, JMP, ADD, NOP, maybe SUB and BNE-like things).

Edited by Nazalassa
Post repurposed.
Link to comment
Share on other sites

Log 23.4

 

I finally got a 256 analog adder working!

KALlogic_029.png

It's pretty straightforward. Two inputs, a carry in, a carry out, and the result. It can be stacked.

 

I then connected it to the bus! So now we have a bus with three registers and some kind of simple ALU.

KALlogic_033.png

KALlogic_035.png

How to operate it: well I already said how transferring stuff between registers work in a post above, so I won't repeat it. By default, the adder processor in enabled and the adder output is disabled. The result has the value of A+B. In order to transfer this result to the A register, here's how to proceed:

  • Disable the adder processor, so that the result won't change;
  • Enable, then disable, the adder output, to transfer the data from it to the A register (make sure the A register isn't reading from the bus);
  • Finally, re-enable the adder processor.

I also added the 'LDC' controller, which has the purpose of loading the C register with whatever's in it, when it's enabled (you'll need to set the value while it is enabled for the value to be actually loaded into C). This way you can load any register by transfering from C to it.

By default the thing is disabled, but it can be left enabled (in fact, you should). Press '0', or whatever key is associated with AG 10, to toggle it.


KerbalX: adder adder+bus

 

After doing all of that I wonder how to actually execute opcodes. Maybe one KAL per opcode? That would play whenever needed and enable / disable KALs as needed.

Edited by Nazalassa
Moved log 23.4 here
Link to comment
Share on other sites

Absolutely awesome work! I don't have much time rn, university exam season and all. but this is actually proving to be feasible.

I was always wondering how a full loop of KALs would work. Usually there's the problem that a KAL cannot be disabled as when you set a KALs position from another KAL it skips around, never playing action groups. But in this case we can just have one single KAL that does actually play with a play speed above zero, essentially acting as our clock. And that one would be the only one allowed to use action groups to set a KAL playing or not for the bus to work. If that's not an option, the slightly messy workaround of having one KAL set the play speed of another KAL which is set to loop and will click an action group if play speed is increased would also work. 

A word of caution though, when playing with complex KAL mechanics on my 1k part concorde I have run into the issue of larger time skips causing issues with the averaging skipping around more. If we end up running into lag we may need to clock down the CPU beyond what the physics tick rate dropping would already do to ensure reliable function. Also, we cannot use axis groups as input, at least not without major filtering. Even when set to playing axis grouped KAL appear to be updating on a different clock cycle, therefore making the averaged output of that KAL and another one flutter between a real average and the value of the non-axis KAL.

By the way, is your adder compatible with negative numbers with my method of just treating the middle of the play positon space as zero?

Link to comment
Share on other sites

On 3/6/2023 at 2:37 AM, HB Stratos said:

Absolutely awesome work! I don't have much time rn, university exam season and all. but this is actually proving to be feasible.

I was always wondering how a full loop of KALs would work. Usually there's the problem that a KAL cannot be disabled as when you set a KALs position from another KAL it skips around, never playing action groups. But in this case we can just have one single KAL that does actually play with a play speed above zero, essentially acting as our clock. And that one would be the only one allowed to use action groups to set a KAL playing or not for the bus to work. If that's not an option, the slightly messy workaround of having one KAL set the play speed of another KAL which is set to loop and will click an action group if play speed is increased would also work. 

A word of caution though, when playing with complex KAL mechanics on my 1k part concorde I have run into the issue of larger time skips causing issues with the averaging skipping around more. If we end up running into lag we may need to clock down the CPU beyond what the physics tick rate dropping would already do to ensure reliable function. Also, we cannot use axis groups as input, at least not without major filtering. Even when set to playing axis grouped KAL appear to be updating on a different clock cycle, therefore making the averaged output of that KAL and another one flutter between a real average and the value of the non-axis KAL.

By the way, is your adder compatible with negative numbers with my method of just treating the middle of the play positon space as zero?

For the adder: unfortunately I guess it isn't. Oh well, if you do the 2's complement-plus-one thing yourself it will probably work, one more piece of hardware to do :)

I think that for running actions we could like have all of them happen in less than 0.04 sec. (one phys-frame) in a single KAL. Like maybe we can have one KAL for each OPcode or something, that plays a sequence that basically enables/disables other KALs. I don't know if enabling then disabling a KAL in the same phys-frame would enable it for a bt, or not. More experiments to do I guess...

I tried a 2-KAL-loop and oh well. The game doesn't seem to like it.

 

EDIT from roughly one day later: I'll try extra-short KALs in the following days to see what we can do it the whole KAL plays in one phys-frame.
 

Edited by Nazalassa
One day later...
Link to comment
Share on other sites

Log 23.5

 

Wheee!

Through testing, I found some interesting stuff: It seems that, as long as we have 1 action or less for 0.04s the sequence is correctly executed. If there are more, some may go missing.
An example is my try at a TAB operation (A->B), it works only if the length of the KAL is 0.08s or more. I don't know what it does if there's some lag. Needs testing I guess.

So, I've added the six transfer instructions to the adder+bus thing, as well as ADD (A+B->A). One KAL is reserved for ADC (ADD with carry). I'll probably add a carry storage once I'll be done with that, should be soon.
 

KALlogic_043.png

This "thing" (tell me if you have a better name) can be either active or inactive, the latter being the default state. It will only do meaningful operations when activated, and to switch between states press '0', or whatever key is associated with AG 10.

The seven possible instructions have been packed in seven different KALs: TAB, TBA, TAC, TCA, TBC, TCB, ADD. Their location is shown in the picture above.

To execute an instruction, simply play the associated KAL. Nothing more!
OK, avoid executing two instructions simultaneously please... That'll break stuff - but otherwise it's safe.

In order to be able to load stuff into the registers, I connected a KAL (labelled "C Register Loader") to the C register. If it is enabled and you write in it, whatever you write will be stored into the C register. You can then use one of the transfer instructions to put that data into another register.


Instruction Set

* TAB : Copies data from the A register into the B register.
* TBA : Copies data from the B register into the A register.
* TAC : Copies data from the A register into the C register.
* TCA : Copies data from the C register into the A register.
* TBC : Copies data from the B register into the C register.
* TCB : Copies data from the C register into the B register.
* ADD : Adds the values of registers A and B, then puts the result back in A. For the moment, there's no carry support.

 

More pictures:

Spoiler

KALlogic_040.png

KALlogic_042.png

Also, if you want to check the 133.4 MiB gif where I add 42+71, it's here: https://imgur.com/vRdED09.gif [sorry, it has been cut by Imgur... :( ]

 

Download: https://kerbalx.com/Nazalassa/256-Analog-Computer-Prototype

Edited by Nazalassa
Moved log 23.5 here
Link to comment
Share on other sites

Super cool stuff, love to see it. I think by now we have proven that a full computer is possible, and I'm excited to see how far we can go. 

I in the meantime have made progress on the parser. It still is very janky, in desperate need of a re-write. It will break on stuff like the panther engine where one engine with two same moduleEngines exist, but otherwise it appears to be working, if very ugly (unused code, unreadable code, outdated comments, etc) . Since it is so heavily WIP I will not throw it on github yet, but if you're interested in using it already and maybe helping me fix stuff, here it is:

Spoiler
from pprint import pprint
import re
import collections.abc # for deep_update


#from https://stackoverflow.com/questions/3232943/update-value-of-a-nested-dictionary-of-varying-depth
def deep_update(target, update):
    for k, v in update.items():
        if isinstance(v, collections.abc.Mapping):
            target[k] = deep_update(target.get(k, {}), v)
        else:
            target[k] = v
    return target



"""
def findFirstLineWithPattern(data:str, pattern:str) -> int :

    pattern:pattern = re.compile(pattern)

    match = re.search(pattern, data)
    if match:
        index = data[:match.start()].count("\n")
    else:
        raise ValueError("no match for pattern found in data")

    return index
"""

def parseCraftHeader(headerString:str) -> dict :
    headerDict = {}

    headerLines:list = headerString.split("\n")

    for line in headerLines:
        if " = " in line:
            key, value = line.split(" = ")
            headerDict[key] = value
        else:
            headerDict[line] = None

    return headerDict


def splitStringByMODULE(string:str) -> list:
    outputList = []

    index = string.find("{")
    if index == -1 : raise ValueError("No { defining a module could be found")
    else:
        previousLineIndex = string.rfind("\n", 0, index)
        outputList = [string[0:previousLineIndex], string[previousLineIndex:]]

    return outputList

def splitIntoSections(string:str) -> list :
    outputList = []

    i = 0
    while True:
        

        i += 1

def _processLine(string:str) -> dict :
    try : key, value = string.split(" = ") 
    except ValueError:  return {string : None}
    else : return {key : value}



def _recurseAddToPartDict(inputDict:dict, nameStack:list[str]) -> dict : 
    if len(nameStack) <= 1 :
        return {nameStack[0] : inputDict}
    else :
        return {nameStack[0] : _recurseAddToPartDict(inputDict, nameStack[1:])}


def recurseParseCraftPart(lineList:list[str]) -> dict:
    

    """
    while True:
        if line = ALLCAPS, followed by {
            find matching closing brace
                throw content into recursion
                remove content from lineList
    
    """
    partDict = {}
    tempDict = {}

    indexStack:list = []
    nameStack:list = []
    processedIndices:list = []
    currentDepth = 0
    previousDepth = 0

    for i, line in enumerate(lineList) :
        if line == "{" : 
            try: lineList[i-1]
            except IndexError: raise ValueError("open brace had no NAME, corruption?")
            indexStack.append(i-1) # do -1 because of NAME before {
            nameStack.append(lineList[i-1])

            previousDepth = currentDepth
            currentDepth +=1
            
        elif line == "}" : 
            start = indexStack.pop()
            currentSection = lineList[start+1:i+1] #i+1 to truncate NAME and to make it inclusive
            currentNAME = nameStack.pop()
            currentDepth -= 1

            

            for localIndex, line in enumerate(currentSection) :
                realIndex = localIndex + start + 1 #+1 needed as we start the currentSection at start+1, lovely off-by-one error
                if realIndex in processedIndices : continue
                if line == "{" or line == "}" : continue
                else: 
                    tempDict["#NAME"] = currentNAME
                    #for line in currentSection: 
                    tempDict.update(_processLine(line))

            for i in range(start, i+1): 
                if i not in processedIndices: processedIndices.append(i)
            
            if nameStack != [] :
                updateDict = _recurseAddToPartDict({currentNAME : tempDict}, nameStack)
                partDict = deep_update(partDict, updateDict)

            print("\n-----\n")
            pprint(tempDict, sort_dicts=False)
            print("\n\n")
            pprint(updateDict, sort_dicts=False)
            print("\n\n")
            pprint(partDict, sort_dicts=False)
            tempDict.clear()

    pprint(partDict, sort_dicts=False)
    return partDict



def parseCraftPart(partString:str) -> dict :
    partDict = {}

    #nestedList = re.findall(r"{(?:[^{}]|(?R))*}", partString)

    #build a nested list of all curly braces
    #loop over all curly braces, handle stuff

    partDict = recurseParseCraftPart(partString.split("\n"))

    return partDict


def parseCraftParts(partsString:str) -> dict : 
    partsDict = {}

    partsList:list = re.split(r"(?=PART\n)", partsString)
    #new_list = [expression for item in iterable if condition]
    partsList = [x for x in partsList if x != ""]

    for part in partsList:
        partsDict.update(parseCraftPart(part))

    return partsDict



def parseCraft(craftData:str) -> dict :
    craftDict = {}

    craftData = craftData.replace('\t', '')

    #create a string slice of craftData containing only the header so we don't have to split the entire string
    headerEnd = re.search("PART",craftData).start()
    headerString:str = craftData[0:headerEnd]
    partsString:str = craftData[headerEnd:] 

    craftDict["header"] = parseCraftHeader(headerString)
    
    craftDict["parts"] = parseCraftParts(partsString)


    return craftDict


craftFile = open('Kal Test.craft', 'r')
print(craftFile)

craftData = craftFile.read() #turns out craft files are not really json, but kinda close

craftDict = parseCraft(craftData)


pprint(craftDict, sort_dicts=False)
print(len(craftDict))

 

 

Edited by HB Stratos
Link to comment
Share on other sites

15 hours ago, HB Stratos said:

Super cool stuff, love to see it. I think by now we have proven that a full computer is possible, and I'm excited to see how far we can go.

Well ,I got working RAM -- the registers. Even if it won't be really usable in large clusters -- three KALs per byte... Er, no, that's too much.

We need to find a way to put more RAM into less KALs, but concatenating two values into one KAL will be impossible, due to potentialprecision errors. Maybe some kind of... Grid? Data compression? I don't know.

 

I think I'll start working on a python script with tk to have a "user-friendly" way to directly edit KAL sequences. I think. I may start working on it in some time. May.

(sorry, not the month)

Link to comment
Share on other sites

Also I think it's time to start thinking about sensors. Like, we can have single-use "same-ship" sensors (i.e sensors that can tell whether they're attached to the ship, or not, in which case they either exploded or were thrown away), but...

 

Input sensors (Action groups) can be made, that's quite easy.

 

As for output, anything that can be controlled by action groups can be made. Like wheel speed and turning, RCS thrusting, engines, ladders, or even science experiments.

Maybe there is a way to trick the game into storing say, a pressure or temperature data into a KAL (with play position)? Maybe. Needs experiments.

Link to comment
Share on other sites

I have done some research on sensors. as far as I know I invented the impact sensor, and make some torpedos with explosion triggers with that. But actual sensors that are more than single use have this far proven to be near impossible. I haven't found a way yet to have something - anything in the environment influence the play position of a KAL. 

Some theories that come to mind would be that we have proven that breaking links is detectable, so maybe breaking a docking port and redocking would be resettable, but I doubt the KAL would gain the curves it lost back. 
So I guess we need to look deeper, perhaps abuse changing tick rates or KAL update rates somehow. I don't know how yet, but that's the best I can think of so far. All my other sensors have been using purely mechanical systems. for example my full auto spoilers don't need a single KAL to function. 

Link to comment
Share on other sites

4 minutes ago, HB Stratos said:

So I guess we need to look deeper, perhaps abuse changing tick rates or KAL update rates somehow. I don't know how yet, but that's the best I can think of so far.

My guess on this is that we need to know how much events can happen in a single phys-tick, however it seems infinite... So um, I don't think we can "push" a KAL's update time by just putting a bunch of other things that add some "latency" (understand: generating enough events to make the KAL not update during this phys-frame).

Now, I think a throttle sensor is possible. Just hook the KAL's play position to the "throttle" axis group (AG) and it will work (I hope).

Also, sensors for player (ship control) input can be done the same way, just hooked to one of the rotate/translate AGs.

Link to comment
Share on other sites

Axis group "sensors" aren't really sensors. They are more like a keyboard, player input. Also because axis groups only respond to player input, and sadly don't respond to SAS and the like. If they did we'd have a real-world sensor right there.

Also worth noting is that the update rate for Axis groups affecting a KAL appears to be different to that of a KAL updating another KAL, which leads to a flickering input if a KAL and an Axis group affect another KAL simultaneously. That sort of tick rate flickering is what I was theorizing that it could be used for some sort of sensor, though I don't know how yet. 

Another thing, there's this section in the KALs craft file: 

Spoiler
        AXISGROUPS
        {
            sequencePosition
            {
                axisGroup = None
                axisIncremental = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
                axisSpeedMultiplier = 0
                axisInverted = None
                overrideIncremental0 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
                overrideIncremental1 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
                overrideIncremental2 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
                overrideIncremental3 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
            }
            sequencePlaySpeed
            {
                axisGroup = None
                axisIncremental = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
                axisSpeedMultiplier = 0
                axisInverted = None
                overrideIncremental0 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
                overrideIncremental1 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
                overrideIncremental2 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
                overrideIncremental3 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04
            }
        }

I wonder whether it would be possible to hack this somehow so that e.g. the position of a sensor would affect the play position of a KAL. sounds near impossible... but maaaaybe..

Link to comment
Share on other sites

So let me get this straight.

You two are building a computer out of modules of KAL-1000 controllers.

It can only sense one type of event.

And it is soon to reach basic 8-bit computer standards.

Please tell me how this works, as I have no idea about how this works. I can tell you how to fix a car, how to build a rocket, and how to wire a house, but I know very little about coding.

Link to comment
Share on other sites

2 hours ago, Jacob Kerman said:

So let me get this straight.

You two are building a computer out of modules of KAL-1000 controllers.

It can only sense one type of event.

And it is soon to reach basic 8-bit computer standards.

Please tell me how this works, as I have no idea about how this works. I can tell you how to fix a car, how to build a rocket, and how to wire a house, but I know very little about coding.

I can't fully explain everything, but you can read up on the basics of what you can do with KAls on my kerbalX posts: https://kerbalx.com/HB_Stratos/Analog-Subtraction , https://kerbalx.com/HB_Stratos/Smoothed-State-Follower

@Nazalassa by the way in my research of craft files I discovered something: 

Look at that movementTransformName... You can enter any transform there. So far I have gotten it to work with model, light_08 and spotlight. The first two make the entire spotlight move including it's base, while the last one only makes the light move without even moving the lamp (It's the spotlight you can pitch and yaw around). I am currently trying to find out which unity function they are using to parse that string, and then try to do path traverse so I can modify parts outside of the current part to move around. Would you have any ideas how any of this could work?

 

MODULE
    {
        name = ModuleLight
        isEnabled = True
        isOn = True
        uiWriteLock = False
        disableColorPicker = False
        lightR = 0.875
        lightG = 0.875
        lightB = 0.875
        castLight = True
        movementTransformName = Lamp
        isBlinking = False
        rotationAngle = 0
        pitchAngle = -22
        blinkRate = 1
        stagingEnabled = True
        EVENTS
        {
Link to comment
Share on other sites

I had assumed that they likely use the unity transform.Find() function, which would allow path traverse with e.g. spotlight_08/Lamp/spotlight, but it doesn't appear to work. With said path traverse ./Lamp should equal Lamp, but only the latter actually works. So I am not entirely sure which function behind the scenes is evaluating this
https://nodachisoft.com/common/en/article/en000181/
https://docs.unity3d.com/ScriptReference/Transform.Find.html

Edited by HB Stratos
Link to comment
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.

×
×
  • Create New...