1

I'm attempting to convert from state vectors (position and speed) into Kepler elements, however I'm running into problems where a negative velocity or position will give me wrong results when trying to calculate true anomaly.

Here are the different ways I'm trying to calculate the True Anomaly:

/// <summary>
/// https://en.wikipedia.org/wiki/True_anomaly#From_state_vectors
/// </summary>
public static double TrueAnomaly(Vector4 eccentVector, Vector4 position, Vector4 velocity)
{
    var dotEccPos = Vector4.Dot(eccentVector, position);
    var talen = eccentVector.Length() * position.Length();
    talen = dotEccPos / talen;
    talen = GMath.Clamp(talen, -1, 1);
    var trueAnomoly = Math.Acos(talen);

    if (Vector4.Dot(position, velocity) < 0)
        trueAnomoly = Math.PI * 2 - trueAnomoly;

    return trueAnomoly;
}
//sgp = standard gravitational parameter
public static double TrueAnomaly(double sgp, Vector4 position, Vector4 velocity)
{
    var H = Vector4.Cross(position, velocity).Length();
    var R = position.Length();
    var q = Vector4.Dot(position, velocity);  // dot product of r*v
    var TAx = H * H / (R * sgp) - 1;
    var TAy = H * q / (R * sgp);
    var TA = Math.Atan2(TAy, TAx);
    return TA;
}
public static double TrueAnomalyFromEccentricAnomaly(double eccentricity, double eccentricAnomaly)
{
    var x = Math.Sqrt(1 - Math.Pow(eccentricity, 2)) * Math.Sin(eccentricAnomaly);
    var y = Math.Cos(eccentricAnomaly) - eccentricity;
    return Math.Atan2(x, y);
}
public static double TrueAnomalyFromEccentricAnomaly2(double eccentricity, double eccentricAnomaly)
{
    var x = Math.Cos(eccentricAnomaly) - eccentricity;
    var y = 1 - eccentricity * Math.Cos(eccentricAnomaly);
    return Math.Acos(x / y);
}

Edit: another way of doing it which Spectre pointed out:

public static double TrueAnomaly(Vector4 position, double loP)
{
    return Math.Atan2(position.Y, position.X) - loP; 
}

Positions are all relative to the parent body.

These functions all agree if position.x, position.y and velocity.y are all positive. How do I fix these so that I get a consistent results when position and velocity are negitive?

Just to clarify: My angles appear to be sort of correct, just pointing in the wrong quadrant depending on the position and or velocity vectors.

Yeah so I was wrong, the above all do return the correct values after all.

So I found an edge case where most of the above calculations fail. Given position and velocity:

pos = new Vector4() { X = -0.208994076275941, Y = 0.955838328099748 };
vel = new Vector4() { X = -2.1678187689294E-07, Y = -7.93096769486992E-08 };

I get some odd results, ie ~ -31.1 degrees, when I think it should return ` 31.1 (non negative). one of them returns ~ 328.8.
However testing with this position and velocity the results apear to be ok:

pos = new Vector4() { X = -0.25, Y = 0.25 };
vel = new Vector4() { X = Distance.KmToAU(-25), Y = Distance.KmToAU(-25) };

See my answer for extra code on how I'm testing and the math I'm using for some of the other variables.

I'm going around in circles on this one. this is a result of a bug in my existing code that shows up under some conditions but not others. I guess the real question now is WHY am I getting different results with position/velocity above that don't match to my expectations or each other?

se5a
  • 97
  • 10

3 Answers3

2

Assuming 2D case... I am doing this differently:

  1. compute radius of semi axises and rotation

    so you need to remember whole orbit and find 2 most distant points on it that is major axis a. The minor axis b usually is 90 deg from major axis but to be sure just fins 2 perpendicularly most distant points on your orbit to major axis. So now you got both semi axises. The initial rotation is computed from the major axis by atan2.

  2. compute true anomaly E

    so if center is x0,y0 (intersection of a,b or center point of both) initial rotation is ang0 (angle of a) and your point on orbit is x,y then:

    E = atan2(y-y0,x-x0) - ang0
    

However in order to match Newton/D'Alembert physics to Kepler orbital parameters you need to boost the integration precision like I did here:

see the [Edit3] Improving Newton D'ALembert integration precision even more in there.

For more info and equations see:

[Edit1] so you want to compute V I see it like this:

img

As you got your coordinates relative to parent you can assume they are already in focal point centered so no need for x0,y0 anymore. Of coarse if you want high precision and have more than 2 bodies (focal mass + object + proximity object(s) like moons) then the parent mass will no longer be in focal point of orbit but close to it ... and to remedy you need to use real focal point position so x0,y0 again... So how to do it:

  1. compute center point (cx,cy) and a,b semi axises

    so its the same as in previous text.

  2. compute focal point (x0,y0) in orbit axis aligned coordinates

    simple:

    x0 = cx + sqrt( a^2 + b^2 );
    y0 = cy;
    
  3. initial angle ang0 of a

    let xa,ya be the intersection of orbit and major axis a on the side with bigger speeds (near parent object focus). Then:

    ang0 = atan2( ya-cy , xa-cx );
    
  4. and finally the V fore any of yours x,y

    V = atan2( y-y0 , x-x0 ) - ang0;
    
Spektre
  • 49,595
  • 11
  • 110
  • 380
  • Basically if I understand that correctly, it should be θ = atan2(ry,rx) - loP where (rx,ry) is the orbiting objects position relative to the parent. – se5a May 27 '19 at 07:47
  • @se5a I added **[edit1]** with `V` computations and also removed obsolete comments ... You can also use the `V = f(E)` equation from the image ... – Spektre May 28 '19 at 10:07
  • Thanks, I deleted some of mine also, and added code to an answer to show how I'm getting various values. I've also found an edge case where my code seems to fall to bits. – se5a May 28 '19 at 21:18
2

Ok so on further testing it appears my original calcs do all return the correct values, however when I was looking at the outputs I was not taking the LoP into account and basically not recognizing that 180 is essentially the same angle as -180. (I was also looking at the output in radians and just didn't see what should have been obvious) Long story short, I have a bug I thought was in this area of the code and got lost in the weeds.
Seems I was wrong above. see OP for edge case.
Here's some code I used to test these,
I used variations of the following inputs:

pos = new Vector4() { X = 0.25, Y = 0.25 };
vel = new Vector4() { X = Distance.KmToAU(-25), Y = Distance.KmToAU(25) };

And tested them with the following

double parentMass = 1.989e30;
double objMass = 2.2e+15;
double sgp = GameConstants.Science.GravitationalConstant * (parentMass + objMass) / 3.347928976e33;

Vector4 ev = OrbitMath.EccentricityVector(sgp, pos, vel);
double e = ev.Length();
double specificOrbitalEnergy = Math.Pow(vel.Length(), 2) * 0.5 - sgp / pos.Length();
double a = -sgp / (2 * specificOrbitalEnergy);
double ae = e * a;
double aop = Math.Atan2(ev.Y, ev.X);
double eccentricAnomaly = OrbitMath.GetEccentricAnomalyFromStateVectors(pos, a, ae, aop);
double aopD = Angle.ToDegrees(aop);
double directAngle = Math.Atan2(pos.Y, pos.X);

var θ1 = OrbitMath.TrueAnomaly(sgp, pos, vel);
var θ2 = OrbitMath.TrueAnomaly(ev, pos, vel);
var θ3 = OrbitMath.TrueAnomalyFromEccentricAnomaly(e, eccentricAnomaly);
var θ4 = OrbitMath.TrueAnomalyFromEccentricAnomaly2(e, eccentricAnomaly);
var θ5 = OrbitMath.TrueAnomaly(pos, aop);
double angleΔ = 0.0000001; //this is the "acceptable" amount of error, really only the TrueAnomalyFromEccentricAnomaly() calcs needed this. 
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ1), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ2), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ3), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ4), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ5), angleΔ);

and the following to compare the angles:

public static double DifferenceBetweenRadians(double a1, double a2)
{
    return Math.PI - Math.Abs(Math.Abs(a1 - a2) - Math.PI);
}

And eccentricity Vector found thus:

public static Vector4 EccentricityVector(double sgp, Vector4 position, Vector4 velocity)
{
    Vector4 angularMomentum = Vector4.Cross(position, velocity);
    Vector4 foo1 = Vector4.Cross(velocity, angularMomentum) / sgp;
    var foo2 = position / position.Length();
    return foo1 - foo2;
}

And EccentricAnomaly:

public static double GetEccentricAnomalyFromStateVectors(Vector4 position, double a, double linierEccentricity, double aop)
{
    var x = (position.X * Math.Cos(-aop)) - (position.Y * Math.Sin(-aop));
    x = linierEccentricity + x;
    double foo = GMath.Clamp(x / a, -1, 1); //because sometimes we were getting a floating point error that resulted in numbers infinatly smaller than -1
    return Math.Acos(foo);
}

Thanks to Futurogogist and Spektre for their help.

se5a
  • 97
  • 10
1

I am assuming you are working in two dimensions?

Two dimensional vectors of position p and velocity v. The constant K is the the product of the gravitational constant and the mass of the gravity generating body. Calculate the eccentricity vector

eccVector = (dot(v, v)*p - dot(v, p)*v) / K - p / sqrt(dot(p, p));

eccentricity = sqrt(dot(eccVector, eccVector));

eccVector = eccVector / eccentricity;

b = { - eccVector.y, eccVector.x};  //unit vector perpendicular to eccVector  

r = sqrt(dot(p, p));

cos_TA = dot(p, eccVector) / r;   \\ cosine of true anomaly
sin_TA = dot(p, b) / r;           \\ sine of true anomaly

if (sin_TA >= 0) { 
   trueAnomaly = arccos(cos_TA);
}
else if (sin_TA < 0){
   trueAnomaly = 2*pi - arccos(cos_TA);
}
Futurologist
  • 1,874
  • 2
  • 7
  • 9
  • This gives a completely different result from all my shown functions. – se5a May 27 '19 at 06:22
  • @se5a This is what is called true anomaly. It is the angle between the great axis of the orbit of the mass point and the position vector, from the focus of the orbit (where the gravity generating body is) and the mass point. I use this calculation in my algorithms, computing the position of satellites including the international space station. My implementation looks like your first implementation. It is possible that your eccentricity vector, which is given as input in your function and it is not calculated, is wrong. – Futurologist May 27 '19 at 13:27
  • for a position of (x=0.25 y=0)au with a velocity vector of y=0.25kms my calcs return 180degrees with an AoP of 180, which sounds about right. your calcs give me 145.478770330931 degrees. I'm calculating eccVec: ``` Vector4 angularMomentum = Vector4.Cross(position, velocity); Vector4 foo1 = Vector4.Cross(velocity, angularMomentum) / sgp; var foo2 = position / position.Length(); return foo1 - foo2;``` which is straight from wikipedia. eccentricity is then the magnitude of the eccVec – se5a May 27 '19 at 20:07
  • @se5a There is no way my formulas yield 145.47 degrees true anomaly. It is obvious that ```dot(p,v) = 0``` because your position and velocity are orthogonal. Hence the eccentricity vector ```eccVector``` is collinear with the position vector ```p```, which means that ```cos_TA = 1 or -1``` i.e. the only possible angles are 0 or 180 degrees. – Futurologist May 27 '19 at 21:02
  • @se5a If I understand your formulas correctly, your and my code are more or less equivalent. However, you have to be very careful about the units of your inputs and the units of your gravitational constant. You are testing your code with positions in astronomical units, while your velocity is km/sec and it is possible your gravitational constant uses meters (although it could be in kilometers too). Are you using Earth's gravitational constant or is it all made up? You know that based on the constant and the mass of the source body, your eccentricity can be different with the same ```p, v```. – Futurologist May 27 '19 at 21:06
  • yeah the calcs are actually all done in au, the velocity gets converted to au in the test code, sgp is also in au, I've got that one down. not sure why my implementation of your code returns something different. I'm actually starting to think that all my calcs are correct after doing more tests and converting the results into degrees. (ie 180 and -180 are the same angle). I need to tweak the tests to be sure. my main problem may lie elsewhere. – se5a May 27 '19 at 21:15
  • @se5a Could it be somewhere in the definition of your dot product and cross product (if you are using one). Since you are in 2D, I avoided using cross product and used the so called BAC-CAB expansion of the triple cross-product. Just to be sure, check again the eccentricity vector definition on wiki: https://en.wikipedia.org/wiki/Eccentricity_vector – Futurologist May 27 '19 at 22:31