Note (2022/03/07): This question is solved. Unfortunately, I'm not able to accept the correct answer by Lutz Lehmann, because I screwed up my registration and the account which posted this question is different from the one I'm using now, even though they have the same name.
I'm trying to find the eccentric anomaly of an orbit through the mean anomaly. I need this to calculate the (2D-)position of an orbiter (called Newton orbiter in the following) at different times. I'm using C# and the Unity game engine.
To control the algorithms correctness, I used Unity physics and a simple gravity simulation to simulate another orbiter (called Unity orbiter in the following) with identical properties. Both orbiters completed their orbits in the same time (which was equal to the calculated orbital period and therefore most likely correct). However, they had different velocities along the path. I printed the specific orbital energy of both orbiters each frame. Only the orbital energy of the Unity orbiter remained constant along the orbit.
Since the path and orbital period of my custom orbital calculation are correct and only the velocity along the path is wrong, I suspect that either the mean anomaly or eccentric anomaly calculation is wrong.
The Unity orbiter reaches its top speed correctly at its periapsis (0.7|-3). The Newton orbiter reaches its top speed at (2|6). The gravity well is at (0|0), both bodies start at (4|0) with initial velocity (1|3).
Potential Problems:
- Unity uses a left-handed coordinate system, this might screw up some formulas
- I do not clamp the time and angles to [0, 2*pi], although I believe it should be fine, because sin and cos are periodic
Used Formulas:
$$ \epsilon = \frac {|v|^2} {2} - \frac {\mu} {|r|} $$
$$ e = (\frac{|v|^2}{\mu} - \frac{1}{|r|}) * r - \frac{r * v}{\mu} * v $$
$$ T = 2 * \pi * \sqrt{\frac{a^3}{\mu}} $$
Mean Anomaly:
$$ M = \frac{t}{T} * 2 * \pi - \phi $$
Newton-Raphson for Eccentric Anomaly:
$$ E_{n+1} = E_n - \frac{f(E_n)}{f'(E_n)} = E_n - \frac{E_n - |e| * sin(E_n) -M}{1 - |e| * cos(E_n)} $$
v - orbiter velocity
$ \mu $ - gravitational parameter (gravity well mass * gravitational constant)
r - orbiter position
a - semimajor axis
t - time since program start in seconds
$ \phi $ - angle between periapsis and x-axis
Code for the Newton orbiter:
public class GravityAnalyzer : MonoBehaviour
{
public const double GRAVITY_CONSTANT = 0.000000000066743;
[SerializeField] private double gravityWellMass = 500.0;
[SerializeField] private Vector2 startVelocity = Vector2.up;
[SerializeField] private int maxIterations = 10;
[SerializeField] private double minPrecision = 0.0001;
private new Rigidbody2D rigidbody = null;
private double gravitationalParameter = 0.0;
private double specificOrbitalEnergy = 0.0;
private double eccentricityMagnitude = 0.0;
private double semiMajorAxis = 0.0;
private double semiMinorAxis = 0.0;
private double phi = 0.0;
private Vector2 orbitCenter = Vector2.zero;
private double orbitalPeriod = 0.0;
Vector2 lastPos = Vector2.zero;
private void Start()
{
rigidbody = gameObject.GetComponent<Rigidbody2D>();
// https://en.wikipedia.org/wiki/Orbital_elements
gravitationalParameter = gravityWellMass * GRAVITY_CONSTANT * 1000000000.0; // Dont mind the magic Number, will be cleaned up later
specificOrbitalEnergy = (startVelocity.sqrMagnitude * 0.5) - (gravitationalParameter / rigidbody.position.magnitude);
Vector2 eccentricity = ((((startVelocity.sqrMagnitude / (float) gravitationalParameter) - (1.0f / rigidbody.position.magnitude)) * rigidbody.position)
- ((Vector2.Dot(rigidbody.position, startVelocity) / (float) gravitationalParameter) * startVelocity));
eccentricityMagnitude = Math.Sqrt((double) eccentricity.x * (double) eccentricity.x + (double) eccentricity.y * (double) eccentricity.y);
// https://en.wikipedia.org/wiki/Semi-major_and_semi-minor_axes#Energy;_calculation_of_semi-major_axis_from_state_vectors
semiMajorAxis = -gravitationalParameter / (2.0 * specificOrbitalEnergy);
semiMinorAxis = Math.Sqrt(1.0 - eccentricityMagnitude * eccentricityMagnitude) * semiMajorAxis;
// https://en.wikipedia.org/wiki/Orbital_period
orbitalPeriod = 2.0 * Math.PI * Math.Sqrt((semiMajorAxis * semiMajorAxis * semiMajorAxis) / gravitationalParameter);
// phi is the Angle by which the Orbit is rotated around the Origin of the Coordinate System
// Vector2.SignedAngle() returns the Result in Degrees, must convert to Radians!
phi = Vector2.SignedAngle(eccentricity, Vector2.right) * (Math.PI / 180.0);
// Calculate the initial Eccentric Anomaly to be able to solve for the Center Point in the next Step
double eccentricAnomaly = CalculateEccentricAnomaly();
// Calculate Center Point of the Ellipse
orbitCenter = -(new Vector2((float) (semiMajorAxis * Math.Cos(phi) * Math.Cos(eccentricAnomaly) - semiMinorAxis * Math.Sin(phi) * Math.Sin(eccentricAnomaly)),
(float) (semiMajorAxis * Math.Sin(phi) * Math.Cos(eccentricAnomaly) + semiMinorAxis * Math.Cos(phi) * Math.Sin(eccentricAnomaly)))
- rigidbody.position);
}
private void FixedUpdate()
{
// Calculate Eccentric Anomaly from current Time
double eccentricAnomaly = CalculateEccentricAnomaly();
// Plug in all Parameters in Parameter-Form of Ellipse-Equation
// https://de.wikipedia.org/wiki/Ellipse#Ellipsengleichung_(Parameterform)
rigidbody.position = orbitCenter
+ new Vector2((float) (semiMajorAxis * Math.Cos(phi) * Math.Cos(eccentricAnomaly) - semiMinorAxis * Math.Sin(phi) * Math.Sin(eccentricAnomaly)),
(float) (semiMajorAxis * Math.Sin(phi) * Math.Cos(eccentricAnomaly) + semiMinorAxis * Math.Cos(phi) * Math.Sin(eccentricAnomaly)));
}
private double CalculateEccentricAnomaly()
{
// Calculate Mean Anomaly
double meanAnomaly = (Time.time / orbitalPeriod) * 2.0 * Math.PI - phi;
// Solve Kepler Equation and convert Mean Anomaly to Eccentric Anomaly
// https://en.wikipedia.org/wiki/Kepler%27s_equation#Numerical_approximation_of_inverse_problem
double eccentricAnomaly = meanAnomaly;
// Skip Calculation if Orbit is circular
if(eccentricityMagnitude > 0.01)
{
// Use different initial Guess for very elliptic Orbits
if(eccentricityMagnitude > 0.8)
{
eccentricAnomaly = Math.PI;
}
// Newton-Raphson
int i = 0;
double lastEccentricAnomaly = 0.0;
do
{
lastEccentricAnomaly = eccentricAnomaly;
eccentricAnomaly = eccentricAnomaly
- ((eccentricAnomaly - eccentricityMagnitude * Math.Sin(eccentricAnomaly) - meanAnomaly)
/ (1.0 - eccentricityMagnitude * Math.Cos(eccentricAnomaly)));
Debug.Log(i + ": Last: " + lastEccentricAnomaly + " Current: " + eccentricAnomaly);
}
while(i++ < maxIterations && !((eccentricAnomaly - lastEccentricAnomaly) < minPrecision && (eccentricAnomaly - lastEccentricAnomaly) > -minPrecision));
}
return eccentricAnomaly;
}
}
Approximate Orbit Sequence:
- Green is the Newton orbiter, red is the Unity orbiter
- Initial Velocity is (1|3) for both
$$ t = 0 $$
$$ t ~= 0.5 * T $$
$$ t ~= 0.9 * T $$