Background:
Attempting to write a game where 'FTL' travel is unaffected by gravity and acceleration is instant.
How do I calculate where a planet will be, given the Kepler orbit for the planet and a ships current position and its maximum FTL speed. (in m/s)
I can get the position of the planet for a given DateTime, but I'm struggling to figure out how to calculate where a planet will be, and where to send the ship to, without chasing the planet around the orbit.
I would iterate...
compute distance between planet and ship current position
from that you compute how much time your ship need to meet the target if the target would be static (not moving). Lets call this time t
.
compute planet position in actual_time+t
and compute t
for this new position
remember last t
lets call it t0
. Then compute new t
in the same way as in #1 but for position of the planet after t
.
loop #2
stop if fabs(t-t0)<accuracy
.
This iterative solution should be closer to the finding t
with each iteration unless your planet moves too fast and/or ship is really too far or too slow (initial t
is significant part or even bigger than the planets tropical year). In such case You usually first jump into the star system and then jump to planet (Like in original Elite).
For obscure planetary movements (like too small orbital period) you would need different methods but realize that such case implies either planet very near to star or very heavy system central mass like black hole...
In code with constant FTL speed it would look like this:
vec3 pp,ps=vec3(?,?,?); // planet and ship positions
double t,t0,time;
time=actual_time(); t=0.0;
for (int i=0;i<100;i++) // just avoiding infinite loop in case t/planet_orbit_period>=~0.5
{
t0=t;
pp = planet_position(time+t);
t=Length(pp-ps)/ship_FTL_speed;
if (fabs(t-t0)*ship_FTL_speed<=ship_safe_FTL_distance) break;
}
This should converge pretty quickly (like 5-10 iterations should be enough) Now t
should hold the time needed for travel and pp
should hold the position your ship should head to. How ever if i>=100
no solution was found so you first need to go closer to the system, or use faster FTL or use different method but that should not be the case for common in stellar system FTL as the travel time should be far less then the targets orbital period...
btw. this might interest you:
- Is it possible to make realistic n-body solar system simulation in matter of size and mass?
[Edit1] slower than FTL translation drive
I give it a bit of taught and change the algo a bit. First it check all the positions along whole planet period with some step (100 points per period) and remember the closest time to travel to the ship regardless of periods of planet passed during the travel. Then simply "recursively" check around best location with smaller and smaller angle step. Here Preview of result:
And updated source (full VCL app code so just use/port what you need and ignore the rest)
//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "win_main.h"
#include "GLSL_math.h" // just for vec3
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMain *Main;
//---------------------------------------------------------------------------
// constants
const double deg=M_PI/180.0;
const double t_day=60.0*60.0*24.0;
// view
double view_x0=0.0;
double view_y0=0.0;
double zoom=1.0;
// simulation
double sim_t=0.0,sim_dt=0.01*t_day;
//---------------------------------------------------------------------------
void toscr(double &x,double &y)
{
x*=zoom; x+=view_x0;
y*=zoom; y+=view_y0;
}
//---------------------------------------------------------------------------
class planet // Kepler body simplified to 2D axis aligned. For fully 3D orbit add mising orbital parameters and equations
{
public:
// input parameters
double a,b,t0,T; // major axis,minor axis, time where M=E=0.0 deg, orbital period
// computet parameters
double c1,c2,e;
void ld(double _a,double _b,double _t0,double _T)
{
// copy input orbital parameters
a=_a;
b=_b;
t0=_t0;
T=_T;
// prepare orbital constants
e=1.0-((b*b)/(a*a)); // eccentricity
if (e>=1.0) e=0; // wrong e
c1=sqrt((1.0+e)/(1.0-e)); // some helper constants computation
c2=a*(1-e*e);
//b=a*sqrt(1.0-e);
}
vec3 position(double t) // actual position relative to center mass of the system
{
int q;
vec3 p;
double E,V,r,M;
// compute mean orbital position M [rad] from time t
M=(t-t0)/T;
M-=floor(M);
M*=2.0*M_PI;
// compute real orbital position E [rad] from M
for (E=M,q=0;q<20;q++) E=M+e*sin(E);// Kepler's equation
// heliocentric ellipse
V=2.0*atan(c1*tan(E/2.0));
r=c2/(1.0+e*cos(V));
p.x=r*cos(V);
p.y=r*sin(V);
p.z=0.0;
return p;
}
void draw_orbit(TCanvas *scr)
{
int i;
double ang,x,y,r,V,E;
x=a; y=0; toscr(x,y);
for (i=2,E=0.0;i;E+=3.6*deg)
{
if (E>=2.0*M_PI) { E=0.0; i=0; }
V=2.0*atan(c1*tan(E/2.0));
r=c2/(1.0+e*cos(V));
x=r*cos(V);
y=r*sin(V);
toscr(x,y);
if (i==2){ scr->MoveTo(x,y); i=1; }
else scr->LineTo(x,y);
}
}
};
//---------------------------------------------------------------------------
class ship // Space ship with translation propulsion
{
public:
vec3 pos,dir; // position and translation direction
double spd,tim; // translation speed and time to translate or 0.0 if no translation
ship() { pos=vec3(0.0,0.0,0.0); dir=pos; spd=0.0; tim=0.0; }
void update(double dt) // simulate dt time step has passed
{
if (tim<=0.0) return;
if (dt>tim) { dt=tim; tim=0.0; }
else tim-=dt;
pos+=spd*dt*dir;
}
void intercept(planet &pl) // set course for planet pl intercept
{
if (spd<=0.0) { tim=0.0; return; }
const double d=1000000.0; // safe distance to target
/*
// [Iteration]
int i;
vec3 p;
double t0;
for (tim=0.0,i=0;i<100;i++)
{
t0=tim;
p=pl.position(sim_t+tim);
tim=length(p-pos)/spd;
if (fabs(tim-t0)*spd<=d) break;
}
dir=normalize(p-pos);
*/
// [search]
vec3 p;
int i;
double tt,t,dt,a0,a1,T;
// find orbital position with min error (coarse)
for (a1=-1.0,t=0.0,dt=0.01*pl.T;t<pl.T;t+=dt)
{
p=pl.position(sim_t+t); // try time t
tt=length(p-pos)/spd;
a0=tt-t; if (a0<0.0) continue; // ignore overshoots
a0/=pl.T; // remove full periods from the difference
a0-=floor(a0);
a0*=pl.T;
if ((a0<a1)||(a1<0.0)) { a1=a0; tim=tt; } // remember best option
}
// find orbital position with min error (fine)
for (i=0;i<3;i++) // recursive increase of accuracy
for (a1=-1.0,t=tim-dt,T=tim+dt,dt*=0.1;t<T;t+=dt)
{
p=pl.position(sim_t+t); // try time t
tt=length(p-pos)/spd;
a0=tt-t; if (a0<0.0) continue; // ignore overshoots
a0/=pl.T; // remove full periods from the difference
a0-=floor(a0);
a0*=pl.T;
if ((a0<a1)||(a1<0.0)) { a1=a0; tim=tt; } // remember best option
}
// direction
p=pl.position(sim_t+tim);
dir=normalize(p-pos);
}
};
//---------------------------------------------------------------------------
planet pl;
ship sh;
//---------------------------------------------------------------------------
void TMain::draw()
{
if (!_redraw) return;
double x,y,r=3;
vec3 p;
// clear buffer
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->FillRect(TRect(0,0,xs,ys));
// Star
bmp->Canvas->Pen->Color=clYellow;
bmp->Canvas->Brush->Color=clYellow;
x=0; y=0; toscr(x,y);
bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
// planet
bmp->Canvas->Pen->Color=clDkGray;
pl.draw_orbit(bmp->Canvas);
bmp->Canvas->Pen->Color=clAqua;
bmp->Canvas->Brush->Color=clAqua;
p=pl.position(sim_t);
x=p.x; y=p.y; toscr(x,y);
bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
// ship
bmp->Canvas->Pen->Color=clRed;
bmp->Canvas->Brush->Color=clRed;
p=sh.pos;
x=p.x; y=p.y; toscr(x,y);
bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
// render backbuffer
Main->Canvas->Draw(0,0,bmp);
_redraw=false;
}
//---------------------------------------------------------------------------
__fastcall TMain::TMain(TComponent* Owner) : TForm(Owner)
{
pl.ld(1000000000.0,350000000.0,0.0,50.0*t_day);
sh.pos=vec3(-3500000000.0,-800000000.0,0.0);
sh.spd=500.0; // [m/s]
sh.intercept(pl);
bmp=new Graphics::TBitmap;
bmp->HandleType=bmDIB;
bmp->PixelFormat=pf32bit;
pyx=NULL;
_redraw=true;
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormDestroy(TObject *Sender)
{
if (pyx) delete[] pyx;
delete bmp;
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormResize(TObject *Sender)
{
xs=ClientWidth; xs2=xs>>1;
ys=ClientHeight; ys2=ys>>1;
bmp->Width=xs;
bmp->Height=ys;
if (pyx) delete[] pyx;
pyx=new int*[ys];
for (int y=0;y<ys;y++) pyx[y]=(int*) bmp->ScanLine[y];
_redraw=true;
view_x0=xs-(xs>>3);
view_y0=ys2;
zoom=double(xs2)/(2.5*pl.a);
// draw(); Sleep(5000);
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormPaint(TObject *Sender)
{
_redraw=true;
}
//---------------------------------------------------------------------------
void __fastcall TMain::tim_redrawTimer(TObject *Sender)
{
for (int i=0;i<10;i++)
{
sh.update(sim_dt);
sim_t+=sim_dt;
if (sh.tim<=0.0) sim_dt=0.0; // stop simulation when jump done
}
if (sim_dt>0.0) _redraw=true;
draw();
}
//---------------------------------------------------------------------------
The important stuff is in the two classes planet,ship
then sim_t
is actual simulated time and sim_dt
is simulated time step. In this case simulation stops after ship reach its destination. The ship::tim
is the time of travel left computed in the ship::intercept()
along with direction for preset speed. The update should be called on each simulated time step ...