1

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.

se5a
  • 97
  • 10

1 Answers1

1

I would iterate...

  1. 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.

  2. 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.

  3. 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:

[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:

intercept

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 ...

Community
  • 1
  • 1
Spektre
  • 49,595
  • 11
  • 110
  • 380
  • This looks like what I was going for, I was halfway there but this is good, will try it out soon. also wanted to check that there wasn't a non iterative calculation that I wasn't aware of, I guess not. – se5a Aug 16 '18 at 07:44
  • @se5a it might exist but it would most likely be a horrible differential equation (or system of those) and as the Kepler is transcendent then you need to use Taylor series instead and after a while you will pile up much more crap than a simple 2 nested for loops with ~5 (iteration) and ~20 (Kepler) cycles each ... – Spektre Aug 16 '18 at 08:26
  • I'm going to mark this as the accepted answer, but how do I handle cases where the travel time is more than the targets orbital period? Also, your link is very interesting some good info in there that I'm bookmarking. – se5a Aug 18 '18 at 21:12
  • @se5a well in such case first jump into vicinity of the planet/system if you are much closer then the initial `t` will be much less. In case of exotic system where planet orbital speeds are really high nearing your FTL speeds you would need to solve this in a different way ... for example you can jump to the orbit and move in opposite way along the orbit until planet is encountered but such cases are hypothetical as 99.999% of the cases will be solved with he first option. – Spektre Aug 18 '18 at 22:01
  • More my speeds may be much lower than c. at early tech. (I'm trying to avoid/put off dealing with Newtonian orbital mechanics for ship travel at this point, and still havn't nailed down a good speed range gameplay wise for the ships, so maybe FTL was a poor choice of wording, maybe 'non-newtonion drive' would have been better) – se5a Aug 18 '18 at 22:10
  • @se5a can you put together a sample edge case? like ship position and "FTL" speed and planet orbital parameters and initial angle ... I think you would need to handle the periodic part of the orbit and then use this iteration on the rest of the path but that would need some testing and proper numbers is alway better than some random data. – Spektre Aug 19 '18 at 08:40
  • @se5a also how you want to deal with the relative speed? Either your ship has strong thrusters with enough fuel and can match speed with target after arrival or you can use "FTL" to position your ship near central Star/mass in position so its gravity pull will match your speed with target and then jump to target. But beware this can take many days (to maintain safe distance from star) but needs no conventional fuel. – Spektre Aug 19 '18 at 08:57
  • This is just within the system, for now I'm going to ignore relative speed and just plonk the ship on a given orbit at the target planet/moon. But eventually if I can make it work, have non-newtonion movement 'store' a ships newtonion velocity on translation start then give it the same velocity at the exit point. then require the ship to expend deltaV to get to the wanted orbit, choosing an orbit to make the maneuver then picking a good spot to exit where the ships velocity will get it closest to the wanted orbit might be interesting. for between systems I'll use something else. – se5a Aug 19 '18 at 10:55
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/178295/discussion-between-se5a-and-spektre). – se5a Aug 19 '18 at 11:02
  • @se5a IIRC beware that chat messages do not notify ... so you must take a look at the chat time to time on your own – Spektre Aug 19 '18 at 12:46
  • @se5a I added [edit1] with a slightly different approach more suited for slow translation. – Spektre Aug 20 '18 at 16:24
  • Thanks for this, I've mostly got it working. and found a bunch of other bugs in the existing code that needed fixing in the process. – se5a Aug 27 '18 at 20:45