Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add orbit constructor and velocityat/positionat #1660

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
484 changes: 242 additions & 242 deletions doc/make.bat

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion doc/source/commands/prediction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ These return predicted information about the future position and velocity of an
:type orbitable: :struct:`Orbitable`
:param time: Time of prediction
:type time: :struct:`TimeSpan`
:return: An :ref:`ObitalVelocity <orbitablevelocity>` structure.
:return: An :ref:`OrbitalVelocity <orbitablevelocity>` structure.

Returns a prediction of what the :ref:`Orbitable's <orbitable>` velocity will be at some :ref:`universal Timestamp <timestamp>`. If the :struct:`Orbitable` is a :struct:`Vessel`, and the :struct:`Vessel` has planned :struct:`maneuver nodes <Node>`, the prediction assumes they will be executed exactly as planned.

Expand Down
61 changes: 59 additions & 2 deletions doc/source/structures/orbits/orbit.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,31 @@ Whenever you get the :struct:`Orbit` of a :struct:`Vessel`, be aware that its ju

Some of the parameters listed here come directly from KSP's API and there is a bit of inconsistency with whether it uses radians or degrees for angles. As much as possible we have tried to present everything in kOS as degrees for consistency, but some of these may have slipped through. If you see any of these being reported in radians, please make a bug report.


Creation
--------

.. function:: ORBIT(x, v, body, t)

:parameter x: (vector) position at time t in the :ref:`ship-center-raw-rotation <ship-raw>` frame
:parameter v: (vetor) velocity at time t
:parameter body: (CelstialBody) central body of orbit
:parameter t: (scalar) universal time
:return: :struct:`Orbit`

This creates a new user defined orbit (for predictive/calculation purposes) around a body given the position and velocity
at a given time. The vectors are in the ref:`ship-center-raw-rotation <ship-raw>` frame. This makes it easy to
recover (or perturb) an orbit based on a given position and velocity:

SET t TO TIME:SECONDS + 100.
SET o TO ORBIT( obt:velocityat(t):orbit, obt:positionat(t), body, t ).

Here, a new :struct:`Orbit` called ``o`` is created that should be very nearly equal to the current orbit.

To create an orbit based on the position (r) and velocity (v) from the center of the body a translation is required:

SET o TO ORBIT( r + body:position, v, body, TIME:SECONDS ).

Copy link
Member

@Dunbaratu Dunbaratu Jun 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if we are going to provide a way to build an Orbit object directly from parameters without a vessel, we should probably also have a second version of this constructor that does it by Keplerian parameters rather than by position/velocity. I'm thinking of the contracts in which you are told to get a satellite into a specific orbit, and you are told the parameters of that orbit. It might be useful for people to be able to construct that orbit object from those parameters.

Structure
---------

Expand Down Expand Up @@ -78,9 +103,15 @@ Structure
* - :attr:`POSITION`
- :struct:`Vector`
- The current position
* - :attr:`POSITIONAT(time)`
- :struct:`Vector`
- The position at the given time
* - :attr:`VELOCITY`
- :struct:`Vector`
- The current velocity
* - :attr:`VELOCITYAT(time)`
- :struct:`Vector`
- The velocity at the given time
* - :attr:`NEXTPATCH`
- :struct:`Orbit`
- Next :struct:`Orbit`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It occurs to me that if we're having suffix-based :POSITIONAT and :VELOCITYAT for OBT objects, we should probably be consistent and make them for BODY and VESSEL too.

The only reason the built-in functions POSITIONAT(thing,time) and VELOCITYAT(thing,time) are not suffixes is purely because they were implemented prior to the introduction of executable suffixes (methods) to the language. Executable suffixes is definitely the more logical way to do them. We can leave the built-in functions there as well for backward compatibility, and behind the scenes redirect both of them to execute the same chunk of code.

Expand Down Expand Up @@ -214,16 +245,42 @@ Structure

:type: :struct:`Vector`
:access: Get only
:return: A position :struct:`Vector` expressed as the coordinates in the :ref:`ship-center-raw-rotation <ship-raw>` frame

The current position of whatever the object is that is in this orbit.
The current position of whatever the object is that is in this orbit. This vector is relative to the :ref:`ship-center-raw-rotation <ship-raw>` frame. It
may be more useful to subtract the orbit:body:position before using it to convert to a body-centric position.

.. attribute:: Orbit:VELOCITY
.. attribute:: Orbit:POSITIONAT

:type: :struct:`Vector`
:param time: Time of prediction
:type time: :struct:`TimeSpan`
:access: Get only
:return: A position :struct:`Vector` expressed as the coordinates in the :ref:`ship-center-raw-rotation <ship-raw>` frame

Returns a prediction of where the object will be at some :ref:`universal Timestamp <timestamp>`. This vector is relative to the
:ref:`ship-center-raw-rotation <ship-raw>` frame (at the current time). It may be more useful to subtract the orbit:body:position before
using it to conver to a body-centric position. This prediction does not take into account future maneuver nodes.

.. attribute:: Orbit:VELOCITY

:type: :struct:`OrbitalVelocity`
:access: Get only
:return: An :ref:`OrbitalVelocity <orbitablevelocity>` structure.

The current velocity of whatever the object is that is in this orbit.

.. attribute:: Orbit:VELOCITYAT

:type: :struct:`OrbitalVelocity`
:param time: Time of prediction
:type time: :struct:`TimeSpan`
:access: Get only
:return: An :ref:`OrbitalVelocity <orbitablevelocity>` structure.

Returns a prediction of what the velocity of the object will be at some :ref:`universal Timestamp <timestamp>`. This prediction does not take into account
future maneuver nodes.

.. attribute:: Orbit:NEXTPATCH

:type: :struct:`Orbit`
Expand Down
127 changes: 127 additions & 0 deletions kerboscript_tests/structures/orbit1.ks
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// useful to debug:
//
// set o to orbit(x, v, body, time:seconds).
// o:positionat(time:seconds + dt).
// o:velocityat(time:seconds + dt).
//
// creates a keosynchronous orbit of an object directly above the equatorial coordinates of
// KSC and checks the position at the current time and 1/4 way around the orbit.


wait until ship:unpacked.
CORE:PART:GETMODULE("kOSProcessor"):DOEVENT("Open Terminal").

local sidereal_day to 5*3600 + 59*60 + 9.4.

// x0, v0 are what we use to build the orbit
local x0 to latlng(0, -75.08333333333333333332):position - ship:body:position.
set x0:mag to (sqrt(kerbin:mu) * sidereal_day / (2 * constant:pi))^(2/3). // 3463331.36.
local v0 to vcrs(kerbin:angularvel, x0).
set v0:mag to sqrt(kerbin:mu / x0:mag).

local keosynch to orbit(x0 + ship:body:position, v0, ship:body, time:seconds).

print "sma : " + keosynch:semimajoraxis.
print "ecc : " + keosynch:eccentricity.
print "lan : " + keosynch:lan.
print "arg : " + keosynch:argumentofperiapsis.
print "period : " + keosynch:period.

// x1, v1 should recover the same, while x2, v2 should be 1/4 around the orbit
local xnow to keosynch:position - ship:body:position.
local vnow to keosynch:velocity:orbit.
local x1 to keosynch:positionat(time:seconds) - ship:body:position.
local v1 to keosynch:velocityat(time:seconds):orbit.
local x2 to keosynch:positionat(time:seconds + sidereal_day/4) - ship:body:position.
local v2 to keosynch:velocityat(time:seconds + sidereal_day/4):orbit.

// this should recover the same orbit
local keosynch2 to orbit(keosynch:positionat(time:seconds + 100), keosynch:velocityat(time:seconds + 100):orbit, ship:body, time:seconds + 100).

// should be the same as xnow/x1
local x3 to keosynch2:positionat(time:seconds) - ship:body:position.
local v3 to keosynch2:velocityat(time:seconds):orbit.


print "x1 : " + x1:mag.
print "v1 : " + v1:mag.
print "x2 : " + x2:mag.
print "v2 : " + v2:mag.

// some handy vectors for debugging in case something goes wrong.
set xvec0 to vecdraw(ship:body:position, x0, rgb(1,0,0), "x0", 1.0, true, 0.2).
set vvec0 to vecdraw(ship:body:position + x0, 1000 * v0, rgb(1,0,0), "v0", 1.0, true, 0.2).
set xvec1 to vecdraw(ship:body:position, x1, rgb(1,0,0), "x1", 1.0, true, 0.2).
set vvec1 to vecdraw(ship:body:position + x1, 1000 * v1, rgb(1,0,0), "v1", 1.0, true, 0.2).
set xvec2 to vecdraw(ship:body:position, x2, rgb(0,1,0), "x2", 1.0, true, 0.2).
set vvec2 to vecdraw(ship:body:position + x2, 1000 * v2, rgb(0,1,0), "v2", 1.0, true, 0.2).

// assertions

if abs(keosynch:period - sidereal_day) > 1
exit.
if abs(keosynch:eccentricity) > 0.000001
exit.
if abs(keosynch:semimajoraxis - 3463331.36) > 0.1
exit.

if abs(keosynch2:period - sidereal_day) > 1
exit.
if abs(keosynch2:eccentricity) > 0.000001
exit.
if abs(keosynch2:semimajoraxis - 3463331.36) > 0.1
exit.

if abs(x0:mag - 3463331.36) > 0.1
exit.
if abs(x1:mag - 3463331.36) > 0.1
exit.
if abs(x2:mag - 3463331.36) > 0.1
exit.
if abs(x3:mag - 3463331.36) > 0.1
exit.

if abs(v0:mag - 1009.81) > 0.1
exit.
if abs(v1:mag - 1009.81) > 0.1
exit.
if abs(v2:mag - 1009.81) > 0.1
exit.
if abs(v3:mag - 1009.81) > 0.1
exit.

// these all recover the same vector at time:seconds ("now")
if (x1 - xnow):mag > 1
exit.
if (v1 - vnow):mag > 1
exit.
if (x1 - x0):mag > 1
exit.
if (v1 - v0):mag > 1
exit.
if (x1 - x3):mag > 1
exit.
if (v1 - v3):mag > 1
exit.

// x2 should point in the v1 direction
if abs(3463331.36 - vdot(x2, v1:normalized)) > 0.1
exit.

// v2 should point in the -x1 direction
if abs(1009.81 - vdot(v2, -x1:normalized)) > 0.1
exit.

// all vectors should be normal to the angular vel of kerbin's rotation
if vdot(x1:normalized, kerbin:angularvel:normalized) > 0.001
exit.
if vdot(v1:normalized, kerbin:angularvel:normalized) > 0.001
exit.
if vdot(x2:normalized, kerbin:angularvel:normalized) > 0.001
exit.
if vdot(v2:normalized, kerbin:angularvel:normalized) > 0.001
exit.
if vdot(x3:normalized, kerbin:angularvel:normalized) > 0.001
exit.
if vdot(v3:normalized, kerbin:angularvel:normalized) > 0.001
exit.
37 changes: 27 additions & 10 deletions src/kOS/Function/Suffixed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,23 @@ public override void Execute(SharedObjects shared)
}
}

[Function("orbit")]
public class FunctionOrbit : FunctionBase
{
public override void Execute(SharedObjects shared)
{
var when = GetTimeSpan(PopValueAssert(shared));
var body = (BodyTarget) PopValueAssert(shared);
if (body == null)
throw new KOSInvalidArgumentException("orbit", "body", "no body found");
Vector vel = GetVector(PopValueAssert(shared));
Vector pos = GetVector(PopValueAssert(shared));
AssertArgBottomAndConsume(shared);
var result = new OrbitInfo(pos, vel, body, when.ToUnixStyleTime(), shared);
ReturnValue = result;
}
}

[Function("heading")]
public class FunctionHeading : FunctionBase
{
Expand Down Expand Up @@ -356,7 +373,7 @@ public override void Execute(SharedObjects shared)
AssertArgBottomAndConsume(shared);
DoExecuteWork(shared, start, vec, rgba, str, scale, show, width);
}

public void DoExecuteWork(SharedObjects shared, Vector start, Vector vec, RgbaColor rgba, string str, double scale, bool show, double width)
{
var vRend = new VectorRenderer( shared.UpdateHandler, shared )
Expand All @@ -369,7 +386,7 @@ public void DoExecuteWork(SharedObjects shared, Vector start, Vector vec, RgbaCo
};
vRend.SetLabel( str );
vRend.SetShow( show );

ReturnValue = vRend;
}
}
Expand All @@ -383,7 +400,7 @@ public override void Execute(SharedObjects shared)
VectorRenderer.ClearAll(shared.UpdateHandler);
}
}

[Function("positionat")]
public class FunctionPositionAt : FunctionBase
{
Expand Down Expand Up @@ -435,7 +452,7 @@ public override void Execute(SharedObjects shared)
ReturnValue = new OrbitInfo( what.GetOrbitAtUT(when.ToUnixStyleTime()), shared );
}
}

[Function("career")]
public class FunctionCareer : FunctionBase
{
Expand All @@ -445,7 +462,7 @@ public override void Execute(SharedObjects shared)
ReturnValue = new Career();
}
}

[Function("constant")]
public class FunctionConstant : FunctionBase
{
Expand All @@ -455,14 +472,14 @@ public override void Execute(SharedObjects shared)
ReturnValue = new ConstantValue();
}
}

[Function("allwaypoints")]
public class FunctionAllWaypoints : FunctionBase
{
public override void Execute(SharedObjects shared)
{
AssertArgBottomAndConsume(shared); // no args

// ReSharper disable SuggestUseVarKeywordEvident
ListValue<WaypointValue> returnList = new ListValue<WaypointValue>();
// ReSharper enable SuggestUseVarKeywordEvident
Expand All @@ -485,7 +502,7 @@ public override void Execute(SharedObjects shared)
ReturnValue = returnList;
}
}

[Function("waypoint")]
public class FunctionWaypoint : FunctionBase
{
Expand All @@ -502,15 +519,15 @@ public override void Execute(SharedObjects shared)
// first time a contract with a waypoint is created).
if (wpm == null)
throw new KOSInvalidArgumentException("waypoint", "\""+pointName+"\"", "no waypoints exist");

string baseName;
int index;
bool hasGreek = WaypointValue.GreekToInteger(pointName, out index, out baseName);
if (hasGreek)
pointName = baseName;
Waypoint point = wpm.Waypoints.FirstOrDefault(
p => string.Equals(p.name, pointName,StringComparison.CurrentCultureIgnoreCase) && (!hasGreek || p.index == index));

// We can't communicate the concept of a lookup fail to the script in a way it can catch (can't do
// nulls), so bomb out here:
if (point ==null)
Expand Down
Loading