Finding surface dynamic information in KOS 0.61 (and above?) Edit

This page shows a way to convert XYZ coordinates of the current SOI space (Sphere of Influence) into a more intuitive coordinate system in which the three axes are East, North, and Up, relative to the point on the planet/moon directly beneath your craft. The code here can be used to, for example, convert VECTOR:SURFACE into its north, east, and up components.

Jumping to the example quickly Edit

To skip the teaching about the coordinate system and just get to the example code, jump to the bottom of the page. If you don't care about the math and just want to treat it like a black box, you can cut and paste it and it works.

Describing the problem this example is meant to solve: Edit

TODO: Much of this description needs to eventually be moved to a different page about the coordinate system in general - perhaps in the sidebar category?

While KOS 0.61 added the ability to get the x,y, and z components of a vector, and also the ability to get the surface-relative prograde vector, instead of being a panacea of usefulness this just exposed an underlying problem in the system: That the native XYZ coordinate system of Kerbal Space Program is hard to work with and not intuitive. Origin poinrts aren't where you'd expect them to be, and the frame of reference shifts based on a number of complex factors. This is made even more problematic for users of KOS than for people writing mod code in KSP's native C# libraries because KOS only exposes a limited set of the underlying data, making it even harder to make the transformation betwen frames of reference.

The native XYZ coordinate system versus latitude, longitude, and UP: Edit

When you think of directions on the Earth, as "North/South/East/West" you probably picture that as a grid in your head. But of course that's not really a grid coordinate system. It's a polar coordinate system, which is why cartographers have to deal with different types of projection when making maps. The north/south/east/west system isn't *really* a flat grid.

Therefore KSP doesn't *really* use it. It converts values into latitude and longitude for display to the user, but internally that's not how things are stored. The space around a planet is really tracked in a rectangular coordinate system, in which "up" at one spot on the equator might be in the positive X direction, while at another spot on the equator 90 degrees of longitude away, the "up" direction might be along the positive Z axis instead. At a point partway between those two points, "up" will contain some X and some Z component to it.

What DO they use (How is the XYZ system centered and oriented)? Edit

(Caveat: Most of this information was discovered experimentally from playing the game and watching velocity numbers change as craft orbit the bodies, and so it might be wrong.)

The space XYZ coordinates have their origin at the center of the planet or moon that your vessel is currently in the SOI of. The axes are oriented as follows:

  • Y axis is always parallel to the body's rotational axis, in its north-pole direction. It is unclear whether this is the solar system's north or the planet's north, as this hasn't been tested on a planet with a large inclination yet.
  • the X-Z plane is the plane that separates the planet into north and south hemispheres, or in other words it's the plane of the equator. The Z axis always come out somewhere exactly 90 degrees west of where the X axis comes out.

TODO: The above could really use a picture here.

BUT, even though you know the Z axis intersection with the planet is always 90 degrees west of the X axis intersection with the planet, you can't predict ahead of time exactly where those are because the location varies depending on where the planet was in its daily rotation at the moment you brought it into focus as the main SOI body and took the planet "off rails'. Wherever it happened to be at that time, that's where the axes are frozen to.

So the chief problem is finding the "real" longitude. Edit

In a sense you can think of the point at which the Z axis intersects the equator as its "real" zero meridian, rather than the one reported as such. If you can find that point, or at least how far off from it you are around the globe, then the rest of the problem reduces to a standard coordinate frame of reference rotation problem, which has known techniques and can be looked up on Wikipedia.

The trick of finding the "real" longitude Edit

When KOS provides the UP rotational tuple, the YAW component of that tuple indirectly tells you the hidden information about where that Z axis actually is, and how far you are rotated around the globe from it. Because the "yaw" portion of the UP rotation is telling you "Up is rotated this far away on the XZ plane from the position at which "up" would have been along the Z axis."

And THAT... *IS* the secret hidden real longitude.

 ( 0 - UP:yaw )

The "latitude: rotation Edit

To find the second rotation, the latitude rotation, you don't need the UP vector. Because the XZ plane IS the equator, THAT rotation isn't tilted at a funny angle. It's just the latitude directly.

The Transformation Matrix Edit

The math for how one can represent a rotation of coordiante systems with a matrix is too large a topic to cover here.  But you can look it up on Wikipedia as a refresher if it's been a while since you had that class, or if you never did and you want to research it for the first time.

So you can rotate a frame of reference using matrix multiplication.  Here we want to rotate first upward by the latitude rotation( around the x axis to move the z axis (which is upward by default when not rotated) up or down), then rotate that around the pole (y axis) using the 'real longitude'.  Doing those two opeartions as matrix rotation, you get the following two matrices multiplied together:

               Matrix to                   Matrix to
  new     =    Rotate around X Axis   X    Rotate around Y     X  old   
  coords       by angle a                  by angle b             coords
              (a = latitude)              (b='real longitude')

 _     _       _                   _      _                     _    _   _
| east  |     |  1    0        0    |    |   cos(b)  0   sin(b)  |  |  x  |
| north |  =  |  0  cos(a)  -sin(a) |    |     0     1    0      |  |  y  |
|_up   _|     |_ 0  sin(a)   cos(a)_|    |_ -sin(b)  0   cos(b) _|  |_ z _|

Putting the two matrices together into one you get:

 _      _       _                                       _   _   _
|  east  |     |     cos(b)        0         sin(b)      | |  x  |
|  north |  =  |   sin(a)sin(b)  cos(a)   -sin(a)cos(b)  | |  y  |
|_ up   _|     |_ -cos(a)sin(b)  sin(a)    cos(a)cos(b) _| |_ z _|

This is the math you see being done in the example program below.

The Example Edit

To try the example, put these two files in your archive, load them onto your vessel, and "run tfXYZtoENUTest."

To save space cut the comments if you like.

The program 'tfXYZtoENU" is intended to be used as a callable subroutine, as you see it being called from tfXYZtoENUTest.

File 1: call it "tfXYZtoENU.txt" Edit

// Given an XYZ coord in the KSP native coord system,
// calculate the same coord in terms of the ENU
// system (ENU is a term I made up for:
// "East North Up".  It's the system with an origin
// point on the surface of the SOI body directly 
// beneath the vessel, and with X=east, Y=north,
// and Z=up.

// Because you can't pass things into or out of
// a program, global variables must be used here
// to simulate that.
// INPUT:  x,y,z,e,n,u
// OUTPUT: tfE,tfN,tfU as global variables.
//   (As of KOS 0.65 there is no way to return a
//   value or pass a variable by reference so globals
//   have to be used for the return values.)
// All "local" variables begin with "tf" to help
// prevent them from clashing with the other
// variables you might have used in the global
// namespace of KOS.

declare parameter tfX,tfY,tfZ.

// Rotation angles for rotation matrix:
set tfA to latitude. 
set tfCosA to cos(tfA).
set tfSinA to sin(tfA).

set tfB to (0 - up:yaw).  // use UP:yaw like it was longitude
set tfCosB to cos(tfB).
set tfSinB to sin(tfB).

// The rotation matrix around z axis (latitude) then y axis (longitude):
set tfW to tfX*tfCosB            + 0          + tfZ*tfSinB            .
set tfN to tfX*tfSinA*tfSinB     + tfY*tfCosA + tfZ*(0-tfSinA*tfCosB) .
set tfU to tfX*(0-tfCosA*tfSinB) + tfY*tfSinA + tfZ*tfCosA*tfCosB     .

// Native XYZ is left-handed-system.  BUT ENU matches the
// Lat/Lon system which is right-handed. So the above rotation
// is calculated for a westerly axis then flipped to east here:
set tfE to 0 - tfW.

File 2: call it "tfXYZtoENUTest.txt" Edit

set mySteer to UP.
lock steering to mySteer.

until 0 {

   set surPro to velocity:surface.
   set xyzLen to ( surPro:x ^2 + surPro:y ^2 + surPro:z ^2 ) ^ 0.5 .
   run tfXYZtoENU( surPro:x, surPro:y, surPro:z ).
   set surProE to tfE.
   set surProN to tfN.
   set surProU to tfU.
   set enuLen to ( surProE^2 + surProN^2 + surProU^2 ) ^ 0.5 .

   print "dumb test example:  Pres CTRL-C to end it.".
   print "Original velocity:surface vector: ".
   print "lat= " + latitude + "up:yaw= " +up:yaw.
   print "      X : " + surPro:x .
   print "      Y : " + surPro:y .
   print "      Z : " + surPro:z .
   print " LENGTH : " + xyzLen.
   print "Transformed into North/East/Up refrence frame: ".
   print "  NORTH : " + surProE.
   print "   EAST : " + surProN.
   print "     UP : " + surProU.
   print " LENGTH : " + enuLen.
   set mySteer to UP * V(0-tfE,tfN,tfU).
   wait 1.