Reference Frames

Introduction

All of the positions, directions, velocities and rotations in kRPC are relative to something, and reference frames define what that something is.

A reference frame specifies:

  • The position of the origin at (0,0,0)

  • the direction of the coordinate axes x, y, and z

  • the linear velocity of the origin (if the reference frame moves)

  • The angular velocity of the coordinate axes (the speed and direction of rotation of the axes)

Note

KSP and kRPC use a left handed coordinate system

Origin Position and Axis Orientation

The following gives some examples of the position of the origin and the orientation of the coordinate axes for various reference frames.

Celestial Body Reference Frame

../_images/celestial-body.png

The reference frame for a celestial body, such as Kerbin. The equator is shown in blue, and the prime meridian in red. The black arrows show the coordinate axes, and the origin is at the center of the planet.

The reference frame obtained by calling CelestialBody.reference_frame for Kerbin has the following properties:

  • The origin is at the center of Kerbin,

  • the y-axis points from the center of Kerbin to the north pole,

  • the x-axis points from the center of Kerbin to the intersection of the prime meridian and equator (the surface position at 0° longitude, 0° latitude),

  • the z-axis points from the center of Kerbin to the equator at 90°E longitude,

  • and the axes rotate with the planet, i.e. the reference frame has the same rotational/angular velocity as Kerbin.

This means that the reference frame is fixed relative to Kerbin – it moves with the center of the planet, and also rotates with the planet. Therefore, positions in this reference frame are relative to the center of the planet. The following code prints out the position of the active vessel in Kerbin’s reference frame:

using System;
using KRPC.Client;
using KRPC.Client.Services.SpaceCenter;

class VesselPosition
{
    public static void Main ()
    {
        using (var connection = new Connection ()) {
            var vessel = connection.SpaceCenter ().ActiveVessel;
            var position = vessel.Position (vessel.Orbit.Body.ReferenceFrame);
            Console.WriteLine ("({0:F1}, {1:F1}, {2:F1})",
                               position.Item1, position.Item2, position.Item3);
        }
    }
}

For a vessel sat on the launchpad, the magnitude of this position vector will be roughly 600,000 meters (equal to the radius of Kerbin). The position vector will also not change over time, because the vessel is sat on the surface of Kerbin and the reference frame also rotates with Kerbin.

Vessel Orbital Reference Frame

../_images/vessel-orbital.png

The orbital reference frame for a vessel.

Another example is the orbital reference frame for a vessel, obtained by calling Vessel.orbital_reference_frame. This is fixed to the vessel (the origin moves with the vessel) and is orientated so that the axes point in the orbital prograde/normal/radial directions.

  • The origin is at the center of mass of the vessel,

  • the y-axis points in the prograde direction of the vessels orbit,

  • the x-axis points in the anti-radial direction of the vessels orbit,

  • the z-axis points in the normal direction of the vessels orbit,

  • and the axes rotate to match any changes to the prograde/normal/radial directions, for example when the prograde direction changes as the vessel continues on its orbit.

Vessel Reference Frame

../_images/vessel-aircraft.png

The reference frame for an aircraft.

Another example is Vessel.reference_frame. As with the previous example, it is fixed to the vessel (the origin moves with the vessel), however the orientation of the coordinate axes is different. They track the orientation of the vessel:

  • The origin is at the center of mass of the vessel,

  • the y-axis points in the same direction that the vessel is pointing,

  • the x-axis points out of the right side of the vessel,

  • the z-axis points downwards out of the bottom of the vessel,

  • and the axes rotate with any changes to the direction of the vessel.

Linear Velocity and Angular Velocity

Reference frames move and rotate relative to one another. For example, the reference frames discussed previously all have their origin position fixed to some object (such as a vessel or a planet). This means that they move and rotate to track the object, and so have a linear and angular velocity associated with them.

For example, the reference frame obtained by calling CelestialBody.reference_frame for Kerbin is fixed relative to Kerbin. This means the angular velocity of the reference frame is identical to Kerbin’s angular velocity, and the linear velocity of the reference frame matches the current orbital velocity of Kerbin.

Available Reference Frames

kRPC provides the following reference frames:

Relative and hybrid reference frames can also be constructed from the above.

Custom Reference Frames

Custom reference frames can be constructed from the built in frames listed above. They come in two varieties: ‘relative’ and ‘hybrid’.

A relative reference frame is constructed from a parent reference frame, a fixed position offset and a fixed rotation offset. For example, this could be used to construct a reference frame whose origin is 10m below the vessel as follows, by applying a position offset of 10 along the z-axis to Vessel.reference_frame. Relative reference frames can be constructed by calling ReferenceFrame.create_relative().

A hybrid reference frame inherits its components (position, rotation, velocity and angular velocity) from the components of other reference frames. Note that these components need not be fixed. For example, you could construct a reference frame whose position is the center of mass of the vessel (inherited from Vessel.reference_frame) and whose rotation is that of the planet being orbited (inherited from CelestialBody.reference_frame). Relative reference frames can be constructed by calling ReferenceFrame.create_hybrid().

The parent reference frame(s) of a custom reference frame can also be other custom reference frames. For example, you could combine the two example frames from above: construct a hybrid reference frame, centered on the vessel and rotated with the planet being orbited, and then create a relative reference that offsets the position of this 10m along the z-axis. The resulting frame will have its origin 10m below the vessel, and will be rotated with the planet being orbited.

Converting Between Reference Frames

kRPC provides utility methods to convert positions, directions, rotations and velocities between the different reference frames:

Visual Debugging

References frames can be confusing, and choosing the correct one is a challenge in itself. To aid debugging, kRPCs drawing functionality can be used to visualize direction vectors in-game.

Drawing.add_direction_from_com() will draw a direction vector, starting from the center of mass of the active vessel. For example, the following code draws the direction of the current vessels velocity relative to the surface of the body it is orbiting:

using System;
using KRPC.Client;
using KRPC.Client.Services.Drawing;
using KRPC.Client.Services.SpaceCenter;

class VisualDebugging
{
    public static void Main ()
    {
        var conn = new Connection ("Visual Debugging");
        var vessel = conn.SpaceCenter ().ActiveVessel;

        var refFrame = vessel.SurfaceVelocityReferenceFrame;
        conn.Drawing ().AddDirectionFromCom(
            new Tuple<double, double, double>(0, 1, 0), refFrame);
        while (true) {
        }
    }
}

Note

The client must remain connected for the line to continue to be drawn, hence the infinite loop at the end of this example.

Examples

The following examples demonstrate various uses of reference frames.

Orbital directions

This example demonstrates how to make the vessel point in the various orbital directions, as seen on the navball when it is in ‘orbit’ mode. It uses Vessel.orbital_reference_frame.

using System;
using System.Collections.Generic;
using System.Net;
using KRPC.Client;
using KRPC.Client.Services.SpaceCenter;

class NavballDirections
{
    public static void Main ()
    {
        using (var conn = new Connection ("Orbital directions")) {
            var vessel = conn.SpaceCenter ().ActiveVessel;
            var ap = vessel.AutoPilot;
            ap.ReferenceFrame = vessel.OrbitalReferenceFrame;
            ap.Engage();

            // Point the vessel in the prograde direction
            ap.TargetDirection = Tuple.Create (0.0, 1.0, 0.0);
            ap.Wait();

            // Point the vessel in the orbit normal direction
            ap.TargetDirection = Tuple.Create (0.0, 0.0, 1.0);
            ap.Wait();

            // Point the vessel in the orbit radial direction
            ap.TargetDirection = Tuple.Create (-1.0, 0.0, 0.0);
            ap.Wait();

            ap.Disengage();
        }
    }
}

This code uses the vessel’s orbital reference frame, pictured below:

../_images/vessel-orbital.png

Surface ‘prograde’

This example demonstrates how to point the vessel in the ‘prograde’ direction on the navball, when in ‘surface’ mode. This is the direction of the vessels velocity relative to the surface:

using System;
using KRPC.Client;
using KRPC.Client.Services.SpaceCenter;

class SurfacePrograde
{
    public static void Main ()
    {
        using (var connection = new Connection (name : "Surface prograde")) {
            var vessel = connection.SpaceCenter ().ActiveVessel;
            var ap = vessel.AutoPilot;

            ap.ReferenceFrame = vessel.SurfaceVelocityReferenceFrame;
            ap.TargetDirection = new Tuple<double,double,double> (0, 1, 0);
            ap.Engage ();
            ap.Wait ();
            ap.Disengage ();
        }
    }
}

This code uses the Vessel.surface_velocity_reference_frame, pictured below:

../_images/vessel-surface-velocity.png

Vessel Speed

This example demonstrates how to get the orbital and surface speeds of the vessel, equivalent to the values displayed by the navball.

To compute the orbital speed of a vessel, you need to get the velocity relative to the planet’s non-rotating reference frame (CelestialBody.non_rotating_reference_frame). This reference frame is fixed relative to the body, but does not rotate.

For the surface speed, the planet’s reference frame (CelestialBody.reference_frame) is required, as this reference frame rotates with the body.

using System;
using KRPC.Client;
using KRPC.Client.Services.SpaceCenter;

class VesselSpeed
{
    public static void Main ()
    {
        var connection = new Connection (name : "Vessel speed");
        var vessel = connection.SpaceCenter ().ActiveVessel;
        var obtFrame = vessel.Orbit.Body.NonRotatingReferenceFrame;
        var srfFrame = vessel.Orbit.Body.ReferenceFrame;
        while (true) {
            var obtSpeed = vessel.Flight (obtFrame).Speed;
            var srfSpeed = vessel.Flight (srfFrame).Speed;
            Console.WriteLine (
                "Orbital speed = {0:F1} m/s, Surface speed = {1:F1} m/s",
                obtSpeed, srfSpeed);
            System.Threading.Thread.Sleep (1000);
        }
    }
}

Vessel Velocity

This example demonstrates how to get the velocity of the vessel (as a vector), relative to the surface of the body being orbited.

To do this, a hybrid reference frame is required. This is because we want a reference frame that is centered on the vessel, but whose linear velocity is fixed relative to the ground.

We therefore create a hybrid reference frame with its rotation set to the vessel’s surface reference frame (Vessel.surface_reference_frame), and all other properties (including position and velocity) set to the body’s reference frame (CelestialBody.reference_frame) – which rotates with the body.

using System;
using KRPC.Client;
using KRPC.Client.Services.SpaceCenter;

class VesselVelocity
{
    public static void Main ()
    {
        var connection = new Connection (name : "Vessel velocity");
        var vessel = connection.SpaceCenter ().ActiveVessel;
        var refFrame = ReferenceFrame.CreateHybrid(
          connection,
          vessel.Orbit.Body.ReferenceFrame,
          vessel.SurfaceReferenceFrame);

        while (true) {
            var velocity = vessel.Flight (refFrame).Velocity;
            Console.WriteLine ("Surface velocity = ({0:F1}, {1:F1}, {2:F1})",
                               velocity.Item1, velocity.Item2, velocity.Item3);
            System.Threading.Thread.Sleep (1000);
        }
    }
}

Angle of attack

This example computes the angle between the direction the vessel is pointing in, and the direction that the vessel is moving in (relative to the surface):

using System;
using KRPC.Client;
using KRPC.Client.Services.SpaceCenter;

class AngleOfAttack
{
    public static void Main ()
    {
        var conn = new Connection ("Angle of attack");
        var vessel = conn.SpaceCenter ().ActiveVessel;

        while (true) {
            var d = vessel.Direction (vessel.Orbit.Body.ReferenceFrame);
            var v = vessel.Velocity (vessel.Orbit.Body.ReferenceFrame);

            // Compute the dot product of d and v
            var dotProd = d.Item1 * v.Item1 + d.Item2 * v.Item2 + d.Item3 * v.Item3;

            // Compute the magnitude of v
            var vMag = Math.Sqrt (
                v.Item1 * v.Item1 + v.Item2 * v.Item2 + v.Item3 * v.Item3);
            // Note: don't need to magnitude of d as it is a unit vector

            // Compute the angle between the vectors
            double angle = 0;
            if (dotProd > 0)
                angle = Math.Abs (Math.Acos (dotProd / vMag) * (180.0 / Math.PI));

            Console.WriteLine (
                "Angle of attack = " + Math.Round (angle, 2) + " degrees");

            System.Threading.Thread.Sleep (1000);
        }
    }
}

Note that the orientation of the reference frame used to get the direction and velocity vectors does not matter, as the angle between two vectors is the same regardless of the orientation of the axes. However, if we were to use a reference frame that moves with the vessel, the velocity would return (0,0,0). We therefore need a reference frame that is not fixed relative to the vessel. CelestialBody.reference_frame fits these requirements.

Landing Site

This example computes a reference frame that is located on the surface of a body at a given altitude, which could be used as the target for a landing auto pilot.

using System;
using KRPC.Client;
using KRPC.Client.Services.SpaceCenter;
using KRPC.Client.Services.Drawing;

class LandingSite
{
    public static void Main ()
    {
        var conn = new Connection ("Landing Site");
        var vessel = conn.SpaceCenter ().ActiveVessel;
        var body = vessel.Orbit.Body;

        // Define the landing site as the top of the VAB
        double landingLatitude = -(0.0+(5.0/60.0)+(48.38/60.0/60.0));
        double landingLongitude = -(74.0+(37.0/60.0)+(12.2/60.0/60.0));
        double landingAltitude = 111;

        // Determine landing site reference frame
        // (orientation: x=zenith, y=north, z=east)
        var landingPosition = body.SurfacePosition(
            landingLatitude, landingLongitude, body.ReferenceFrame);
        var qLong = Tuple.Create(
          0.0,
          Math.Sin(-landingLongitude * 0.5 * Math.PI / 180.0),
          0.0,
          Math.Cos(-landingLongitude * 0.5 * Math.PI / 180.0)
        );
        var qLat = Tuple.Create(
          0.0,
          0.0,
          Math.Sin(landingLatitude * 0.5 * Math.PI / 180.0),
          Math.Cos(landingLatitude * 0.5 * Math.PI / 180.0)
        );
        var landingReferenceFrame =
          ReferenceFrame.CreateRelative(
            conn,
            ReferenceFrame.CreateRelative(
              conn,
              ReferenceFrame.CreateRelative(
                conn,
                body.ReferenceFrame,
                landingPosition,
                qLong),
              Tuple.Create(0.0, 0.0, 0.0),
              qLat),
            Tuple.Create(landingAltitude, 0.0, 0.0));

        // Draw axes
        var zero = Tuple.Create(0.0, 0.0, 0.0);
        conn.Drawing().AddLine(
            zero, Tuple.Create(1.0, 0.0, 0.0), landingReferenceFrame);
        conn.Drawing().AddLine(
            zero, Tuple.Create(0.0, 1.0, 0.0), landingReferenceFrame);
        conn.Drawing().AddLine(
            zero, Tuple.Create(0.0, 0.0, 1.0), landingReferenceFrame);

        while (true)
          System.Threading.Thread.Sleep (1000);
    }
}