Posición incorrecta durante la propagación de la órbita en Unity

He estado trabajando en un módulo de física orbital durante meses y he llegado a una barrera. Tengo dos métodos, uno para convertir elementos de Kepler a coordenadas planetarias, y el otro para determinar los elementos de Kepler a partir de los vectores de estado R y V. El PositionFromOrbit()método propaga el cuerpo alrededor de la órbita, pero comienza desplazado de su lugar inicial en la órbita. Luego, después de un período, comienza a saltar de un punto a otro en la órbita. Aquí hay un ejemplo:ingrese la descripción de la imagen aquí

El cubo azul es la posición actual del objeto antes del tiempo de ejecución, y el contorno rojo es la posición calculada del objeto según sus elementos keplerianos. Puedo ver que el contorno está exactamente encima del apoapsis, pero no sé cómo ni por qué. Aquí están los fragmentos principales, comenzando con la conversión de vectores de estado a elementos keplerianos:

public static KeplerOrbit OrbitFromState(Vector3Decimal R, Vector3Decimal V, decimal mu)
        {
            Vector3Decimal h = Vector3Decimal.Cross(R, V);
            Vector3Decimal n = Vector3Decimal.Cross(Vector3Decimal.forward, h);
            Vector3Decimal eVec = ((V.magnitude * V.magnitude - mu / R.magnitude) * R - Vector3Decimal.Dot(R, V) * V) / mu; // eccentricity vector
            decimal eMag = eVec.magnitude;
            decimal E = V.magnitude * V.magnitude / 2 - mu / R.magnitude;
            decimal a;
            decimal p;
            if(eVec.magnitude != 1)
            {
                a = -mu / (2 * E);
                p = a * (1 - eMag * eMag);
            }
            else
            {
                a = 0;
                p = h.magnitude * h.magnitude / mu;
            }
            decimal inc = ACos(h.z / h.magnitude);

            decimal O;
            if(inc == 0 || inc == Pi)
            {
                O = 0;
            }
            else
            {
                O = ACos(n.x / n.magnitude);
            }

            if(n.y < 0)
            {
                O = 2 * Pi - O;
            }

            decimal w;
            if (eVec.magnitude == 0)
            {
                w = 0;
            }
            else
            {
                if (inc == 0 || inc == Pi)
                {
                    w = ATan2(eVec.y, eVec.x);
                }
                else
                {
                    w = ACos(Vector3Decimal.Dot(n, eVec) / (n.magnitude * eVec.magnitude));
                }
            }
            if(eVec.z < 0)
            {
                w = 2 * Pi - w;
            }

            decimal nu;
            if(eVec.magnitude == 0)
            {
                if(inc == 0 || inc == Pi)
                {
                    nu = ACos(R.z / R.magnitude);
                }
                else
                {
                    nu = ACos(Vector3Decimal.Dot(n, R) / (n.magnitude * R.magnitude));
                }
            }
            else
            {
                nu = ACos(Vector3Decimal.Dot(eVec, R) / (eVec.magnitude * R.magnitude));
                if(Vector3Decimal.Dot(R,V) < 0)
                {
                    nu = 2 * Pi - nu;
                }
            }

            KeplerOrbit ret = new KeplerOrbit(E, a, eVec.magnitude, inc, O, w, nu);
            return ret;

        }

Y aquí está el método para encontrar su posición:

public static Vector3Decimal PositionFromOrbit(KeplerOrbit orbit, decimal t, decimal mu)
        {
            decimal a = orbit.semi_major_axis;
            decimal e = orbit.eccentricity;
            decimal i = orbit.inclination;
            decimal O = orbit.longitude_of_ascent;
            decimal w = orbit.argument_of_periapsis;
            //decimal T = Period(orbit.semi_major_axis, mu);
            decimal t0 = TimeAtEccentricAnomaly(a, e, 0, mu);
            decimal Mt;
            if(t == t0)
            {
                t = t0;
                Mt = 0;
            }
            else
            {
                decimal deltaT = (t - t0);
                Mt = deltaT * Sqrt(mu / (a * a * a));
            }

            decimal E = Mt;
            decimal F = E - e * Sin(E) - Mt;
            int j = 0;
            int maxIter = 30;
            decimal delta = 0.000001m;
            while(Abs(F) > delta && j < maxIter)
            {
                //E = (E - F / (1 - e * Cos(E))) % (Pi * 2);
                E = (E - F / (1 - e * Cos(E))); // this is the line that fixed jittering.
                F = E - e * Sin(E) - Mt;
                j++;
            }

            decimal nu = 2 * ATan2(Sqrt(1 + e) * Sin(E / 2), Sqrt(1 - e) * Cos(E / 2));
            decimal rc = a * (1 - e * Cos(E));

            Vector3Decimal o = new Vector3Decimal(rc * Cos(nu), rc * Sin(nu), 0);
            Vector3Decimal odot = new Vector3Decimal(Sin(E), Sqrt(1 - e * e) * Cos(E), 0);
            odot = odot * (Sqrt(mu * a) / rc);
            decimal rx, ry, rz;
            rx = o.x; ry = o.y; rz = o.z;

            rx = (o.x * (Cos(w) * Cos(O) - Sin(w) * Cos(i) * Sin(O)) - o.y * (Sin(w) * Cos(O) + Cos(w) * Cos(i) * Sin(O)));
            ry = (o.x * (Cos(w) * Sin(O) + Sin(w) * Cos(i) * Cos(O)) + o.y * (Cos(w) * Cos(i) * Cos(O) - Sin(w) * Sin(O)));
            rz = (o.x * (Sin(w) * Sin(i)) + o.y * (Cos(w) * Sin(i)));

            Vector3Decimal r = new Vector3Decimal(rx, ry, rz);
            return r;

        }

¿Alguien puede detectar dónde el yo o los métodos están fallando en su tarea?

Mis vectores iniciales: R = 15, -0.07, 3.36 V = -0.39, 5.14, -0.04

He tenido este problema durante algunas semanas y sería un gran avance si se solucionara. Intenté agregar/restar el argumento de la periapsis de la verdadera anomalía como un novato, pero hizo que la órbita fuera inexacta. Intenté relacionar la posición con el momento del periápside y tampoco funcionó. Adopté otros tres métodos que no funcionaron tan bien como el actual.

¡Gracias a todos!

Editar: Encontré otro síntoma del problema u otro: Después de una órbita suave, el objeto comienza a saltar alrededor de su órbita.después de una órbita suave, el objeto comienza a saltar alrededor de su órbita. Viaja del periapsis a la apoapsis y viceversa antes de que comience a temblar.

Editar: El nerviosismo se ha resuelto, gracias a notovny, quien recomendó que lo elimine % (2 * Pi)al resolver la anomalía excéntrica.

¿Qué tan excéntrica e inclinada es su órbita de prueba? Proporcione los parámetros utilizados para inicializar la órbita de prueba que se muestra en la figura. ¿Se enfrenta a los mismos problemas cuando realiza pruebas con una órbita muy inclinada y muy excéntrica?
Lo único que me llama la atención: si mi es anomalía excéntrica, y w es el Argumento de Periapsis, no deberías sumarlos; La anomalía excéntrica es completamente independiente del argumento del periapsis.
@AJN La órbita de prueba estaba ligeramente inclinada, pero acabo de probar una órbita con una inclinación de unos 40 grados y otra muy cercana a los 90 grados, y ambas continuaron teniendo los mismos síntomas con una diferencia imperceptible.
@notovny oopsie, saqué esa línea y la probé de nuevo... no hay diferencia, desafortunadamente.
La otra cosa que parece extraña es que intentar rectificar mi a la gama ( 0 , 2 π ] con el operador de módulo dentro del ciclo del método de Newton es probablemente una mala idea, ya que toma una función suave y arroja una discontinuidad periódica en ella para que Newton-Raphson se tropiece cada vez que se completa una órbita. Probablemente lo eliminaría % (Pi * 2)por completo. También secundó la solicitud de AJN de un conjunto de parámetros que producen el comportamiento indeseable, así como enlaces a la referencia que usó para producir estas ecuaciones.
@notovny ¡Ajá! Eliminar ` % (Pi * 2)` eliminó los locos temblores y saltos. @AJN Mis parámetros iniciales son los vectores de estado, R = 15, -0.07, 3.36 y V = -0.39, 5.14, -0.04. El objeto todavía está desplazado de su posición inicial, pero tengo esperanzas después de que se detuviera el nerviosismo.
La fuente principal de los métodos está aquí: space.stackexchange.com/questions/19322/…
Eliminando el comentario anterior: parece que el OrbitFromStatemétodo almacena una anomalía verdadera en el valor de época como nu. Dicho esto, aún falta ver el KeplerOrbitobjeto completo y lo que TimeAtEccentricAnomalyhace el método para diagnosticar; Si hago suposiciones probables, parece que su método actual solo PositionFromOrbitdará respuestas correctas si sus vectores de velocidad y distancia radial sonR para Vel tiempo t = 0 en OrbitFromStatey apuntan al periapsis. R

Respuestas (1)

Si desea una propagación de órbita de alta fidelidad, debe almacenar la órbita en forma cartesiana porque es una representación de órbita no singular. También es la forma más sencilla de tener en cuenta las fuerzas adicionales en la nave espacial porque solo necesita integrar F = metro a y aplicarlo al estado cartesiano sin tratar de averiguar cómo la fuerza dada cambia la excentricidad, el semieje mayor, etc.

Para la conversión de representación kepleriana a cartesiana, el algoritmo no es tan sencillo (si desea tener en cuenta todas las órbitas posibles). Aquí hay un enlace a la función exacta en el código fuente GMAT C ++ (148 líneas C ++) y aquí está la "traducción" en Nyx (80 líneas en Rust, que me parece más clara). Para confirmar que esta traducción es correcta, aquí está el enlace a la validación de esa función en Nyx .

Aclaro que estas funciones son para propagar órbitas sobre rieles con las que no se interactuará mucho. Estoy creando los métodos con alta precisión, pero los copiaré para lograr una precisión flotante. El objetivo de esta conversión es poder almacenar la órbita y no hacer referencia a ella durante mucho tiempo hasta que necesite ser referenciada.
Además, se utilizarán para orbitar estrellas alrededor de un centro galáctico, por lo que la precisión es bastante necesaria.
No estoy seguro de entender tu comentario. ¿Qué quiere decir con órbitas "sobre rieles"? Además, el almacenamiento de órbitas en cartesiano es un método que permite un almacenamiento de alta precisión sin errores matemáticos (que no es el caso de un conjunto de elementos orbitales keplerianos).
Ah, debería explicarlo. Sobre rieles ( on_railses lo que booleantengo en todos los cuerpos astrofísicos), las órbitas no están sujetas a perturbaciones y pueden propagarse durante largos períodos de tiempo o pasos de tiempo sin errores, lo que le permite ignorarlos, así como también trazar maniobras y predecir eventos. como LOD de visualización (nivel de detalle). Los cuerpos fuera de rieles utilizarán la física de Unity y estarán sujetos a perturbaciones. Estoy usando un origen flotante para mantener al jugador a baja velocidad en la escena.
Todas estas cosas son muy importantes ya que estoy intentando generar estrellas dentro de una galaxia a escala (no generando cada estrella, solo campos de densidad y probabilidad), por lo que he creado un tipo de datos y tengo los elementos Kep almacenados Vector3Decimalen decimal. Encontré la herramienta que necesito para el trabajo, solo tengo problemas para implementarla. Muchos de estos métodos y tipos de datos tendrán floatformularios y serán asincrónicos para facilitar el trabajo del procesador. Espero que esto tenga sentido.