/* Copyright 2002-2017 CS Systèmes d'Information * Licensed to CS Systèmes d'Information (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * CS licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.orekit.orbits; import java.util.Collection; import org.hipparchus.RealFieldElement; import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator; import org.hipparchus.exception.MathIllegalArgumentException; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathArrays; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.TimeStampedFieldPVCoordinates; /** * This class handles traditional keplerian orbital parameters. * <p> * The parameters used internally are the classical keplerian elements: * <pre> * a * e * i * ω * Ω * v * </pre> * where ω stands for the Perigee Argument, Ω stands for the * Right Ascension of the Ascending Node and v stands for the true anomaly. * </p> * <p> * This class supports hyperbolic orbits, using the convention that semi major * axis is negative for such orbits (and of course eccentricity is greater than 1). * </p> * <p> * When orbit is either equatorial or circular, some keplerian elements * (more precisely ω and Ω) become ambiguous so this class should not * be used for such orbits. For this reason, {@link EquinoctialOrbit equinoctial * orbits} is the recommended way to represent orbits. * </p> * <p> * The instance <code>KeplerianOrbit</code> is guaranteed to be immutable. * </p> * @see Orbit * @see CircularOrbit * @see CartesianOrbit * @see EquinoctialOrbit * @author Luc Maisonobe * @author Guylaine Prat * @author Fabien Maussion * @author Véronique Pommier-Maurussane * @author Andrea Antolino */ public class FieldKeplerianOrbit<T extends RealFieldElement<T>> extends FieldOrbit<T> { /** First coefficient to compute Kepler equation solver starter. */ private static final double A; /** Second coefficient to compute Kepler equation solver starter. */ private static final double B; static { final double k1 = 3 * FastMath.PI + 2; final double k2 = FastMath.PI - 1; final double k3 = 6 * FastMath.PI - 1; A = 3 * k2 * k2 / k1; B = k3 * k3 / (6 * k1); } /** Semi-major axis (m). */ private final T a; /** Eccentricity. */ private final T e; /** Inclination (rad). */ private final T i; /** Perigee Argument (rad). */ private final T pa; /** Right Ascension of Ascending Node (rad). */ private final T raan; /** True anomaly (rad). */ private final T v; /** Element identité. */ private final T one; /**Element zero. */ private final T zero; /** Third Canonical Vector. */ private final FieldVector3D<T> PLUS_K; /** Creates a new instance. * @param a semi-major axis (m), negative for hyperbolic orbits * @param e eccentricity * @param i inclination (rad) * @param pa perigee argument (ω, rad) * @param raan right ascension of ascending node (Ω, rad) * @param anomaly mean, eccentric or true anomaly (rad) * @param type type of anomaly * @param frame the frame in which the parameters are defined * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame}) * @param date date of the orbital parameters * @param mu central attraction coefficient (m³/s²) * @exception IllegalArgumentException if frame is not a {@link * Frame#isPseudoInertial pseudo-inertial frame} or a and e don't match for hyperbolic orbits, * or v is out of range for hyperbolic orbits */ public FieldKeplerianOrbit(final T a, final T e, final T i, final T pa, final T raan, final T anomaly, final PositionAngle type, final Frame frame, final FieldAbsoluteDate<T> date, final double mu) throws IllegalArgumentException { super(frame, date, mu); if (a.multiply(e.negate().add(1)).getReal() < 0) { throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_A_E_MISMATCH_WITH_CONIC_TYPE, a, e); } this.a = a; this.e = e; this.i = i; this.pa = pa; this.raan = raan; /**Element identité.*/ this.one = a.getField().getOne(); /**Element zero.*/ this.zero = a.getField().getZero(); /**Third canonical vector. */ this.PLUS_K = new FieldVector3D<T>(zero, zero, one); final T tmpV; switch (type) { case MEAN : tmpV = (a.getReal() < 0) ? hyperbolicEccentricToTrue(meanToHyperbolicEccentric(anomaly, e)) : ellipticEccentricToTrue(meanToEllipticEccentric(anomaly)); break; case ECCENTRIC : tmpV = (a.getReal() < 0) ? hyperbolicEccentricToTrue(anomaly) : ellipticEccentricToTrue(anomaly); break; case TRUE : tmpV = anomaly; break; default : // this should never happen throw new OrekitInternalError(null); } // check true anomaly range if (e.multiply(tmpV.cos()).add(1).getReal() <= 0) { final T vMax = e.reciprocal().negate().acos(); throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_ANOMALY_OUT_OF_HYPERBOLIC_RANGE, tmpV, e, vMax.negate(), vMax); } this.v = tmpV; } /** Constructor from cartesian parameters. * * <p> The acceleration provided in {@code FieldPVCoordinates} is accessible using * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods * use {@code mu} and the position to compute the acceleration, including * {@link #shiftedBy(RealFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}. * * @param FieldPVCoordinates the PVCoordinates of the satellite * @param frame the frame in which are defined the {@link FieldPVCoordinates} * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame}) * @param mu central attraction coefficient (m³/s²) * @exception IllegalArgumentException if frame is not a {@link * Frame#isPseudoInertial pseudo-inertial frame} */ public FieldKeplerianOrbit (final TimeStampedFieldPVCoordinates<T> FieldPVCoordinates, final Frame frame, final double mu) throws IllegalArgumentException { super(FieldPVCoordinates, frame, mu); /**Element identité*/ this.one = FieldPVCoordinates.getPosition().getX().getField().getOne(); /**Element zero*/ this.zero = this.one.getField().getZero(); /**Third canonical vector */ this.PLUS_K = new FieldVector3D<T>(zero, zero, one); // compute inclination final FieldVector3D<T> momentum = FieldPVCoordinates.getMomentum(); final T m2 = momentum.getNormSq(); i = FieldVector3D.angle(momentum, PLUS_K); // compute right ascension of ascending node raan = FieldVector3D.crossProduct(PLUS_K, momentum).getAlpha(); // preliminary computations for parameters depending on orbit shape (elliptic or hyperbolic) final FieldVector3D<T> pvP = FieldPVCoordinates.getPosition(); final FieldVector3D<T> pvV = FieldPVCoordinates.getVelocity(); final T r = pvP.getNorm(); final T V2 = pvV.getNormSq(); final T rV2OnMu = r.multiply(V2).divide(mu); // compute semi-major axis (will be negative for hyperbolic orbits) a = r.divide(rV2OnMu.negate().add(2.0)); final T muA = a.multiply(mu); // compute true anomaly if (a.getReal() > 0) { // elliptic or circular orbit final T eSE = FieldVector3D.dotProduct(pvP, pvV).divide(muA.sqrt()); final T eCE = rV2OnMu.subtract(1); e = (eSE.multiply(eSE).add(eCE.multiply(eCE))).sqrt(); v = ellipticEccentricToTrue(eSE.atan2(eCE)); //(atan2(eSE, eCE)); } else { // hyperbolic orbit final T eSH = FieldVector3D.dotProduct(pvP, pvV).divide(muA.negate().sqrt()); final T eCH = rV2OnMu.subtract(1); e = (m2.negate().divide(muA).add(1)).sqrt(); v = hyperbolicEccentricToTrue((eCH.add(eSH)).divide(eCH.subtract(eSH)).log().divide(2)); } // compute perigee argument final FieldVector3D<T> node = new FieldVector3D<T>(raan, zero); final T px = FieldVector3D.dotProduct(pvP, node); final T py = FieldVector3D.dotProduct(pvP, FieldVector3D.crossProduct(momentum, node)).divide(m2.sqrt()); pa = py.atan2(px).subtract(v); } /** Constructor from cartesian parameters. * * <p> The acceleration provided in {@code FieldPVCoordinates} is accessible using * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods * use {@code mu} and the position to compute the acceleration, including * {@link #shiftedBy(RealFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}. * * @param FieldPVCoordinates the PVCoordinates of the satellite * @param frame the frame in which are defined the {@link FieldPVCoordinates} * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame}) * @param date date of the orbital parameters * @param mu central attraction coefficient (m³/s²) * @exception IllegalArgumentException if frame is not a {@link * Frame#isPseudoInertial pseudo-inertial frame} */ public FieldKeplerianOrbit(final FieldPVCoordinates<T> FieldPVCoordinates, final Frame frame, final FieldAbsoluteDate<T> date, final double mu) throws IllegalArgumentException { this(new TimeStampedFieldPVCoordinates<T>(date, FieldPVCoordinates), frame, mu); } /** Constructor from any kind of orbital parameters. * @param op orbital parameters to copy */ public FieldKeplerianOrbit(final FieldOrbit<T> op) { this(op.getPVCoordinates(), op.getFrame(), op.getDate(), op.getMu()); } /** {@inheritDoc} */ public OrbitType getType() { return OrbitType.KEPLERIAN; } /** {@inheritDoc} */ public T getA() { return a; } /** {@inheritDoc} */ public T getE() { return e; } /** {@inheritDoc} */ public T getI() { return i; } /** Get the perigee argument. * @return perigee argument (rad) */ public T getPerigeeArgument() { return pa; } /** Get the right ascension of the ascending node. * @return right ascension of the ascending node (rad) */ public T getRightAscensionOfAscendingNode() { return raan; } /** Get the anomaly. * @param type type of the angle * @return anomaly (rad) */ public T getAnomaly(final PositionAngle type) { return (type == PositionAngle.MEAN) ? getMeanAnomaly() : ((type == PositionAngle.ECCENTRIC) ? getEccentricAnomaly() : getTrueAnomaly()); } /** Get the true anomaly. * @return true anomaly (rad) */ public T getTrueAnomaly() { return v; } /** Get the eccentric anomaly. * @return eccentric anomaly (rad) */ public T getEccentricAnomaly() { if (a.getReal() < 0) { // hyperbolic case final T sinhH = (e.multiply(e).subtract(1)).sqrt().multiply(v.sin().divide(e.multiply(v.cos()).add(1))); return sinhH.asinh(); } // elliptic case final T beta = e.divide((e.subtract(1).negate().multiply(e.add(1))).sqrt().add(1)); return v.subtract((beta.multiply(v.sin()).divide(beta.multiply(v.cos()).add(1))).atan().multiply(2)); } /** Computes the true anomaly from the elliptic eccentric anomaly. * @param E eccentric anomaly (rad) * @return v the true anomaly */ private T ellipticEccentricToTrue(final T E) { final T beta = e.divide(e.negate().add(1).multiply(e.add(1)).sqrt().add(1)); return E.add(beta.multiply(E.sin()).divide(beta.multiply(E.cos()).negate().add(1)).atan().multiply(2)); } /** Computes the true anomaly from the hyperbolic eccentric anomaly. * @param H hyperbolic eccentric anomaly (rad) * @return v the true anomaly */ private T hyperbolicEccentricToTrue(final T H) { // return 2 * FastMath.atan(FastMath.sqrt((e + 1) / (e - 1)) * FastMath.tanh(H / 2)); return ((e.add(1).divide(e.subtract(1))).sqrt().multiply(H.divide(2.0).tanh())).atan().multiply(2); } /** Get the mean anomaly. * @return mean anomaly (rad) */ public T getMeanAnomaly() { if (a.getReal() < 0) { // hyperbolic case final T H = getEccentricAnomaly(); return e.multiply(H.sinh()).subtract(H); } // elliptic case final T E = getEccentricAnomaly(); return E.subtract(e.multiply(E.sin())); } /** Computes the elliptic eccentric anomaly from the mean anomaly. * <p> * The algorithm used here for solving Kepler equation has been published * in: "Procedures for solving Kepler's Equation", A. W. Odell and * R. H. Gooding, Celestial Mechanics 38 (1986) 307-334 * </p> * @param M mean anomaly (rad) * @return v the true anomaly */ private T meanToEllipticEccentric(final T M) { //SUBSTITUTED THE NORMALIZE ANGLE FUNCTION // reduce M to [-PI PI) interval final T reducedM = normalizeAngle(M, this.zero); // // compute start value according to A. W. Odell and R. H. Gooding S12 starter T E; if (reducedM.abs().getReal() < 1.0 / 6.0) { E = reducedM.add(e.multiply( (reducedM.multiply(6).cbrt()).subtract(reducedM))); } else { if (reducedM.getReal() < 0) { final T w = reducedM.add(FastMath.PI); E = reducedM.add(e.multiply(w.multiply(A).divide(w.negate().add(B)).subtract(FastMath.PI).subtract(reducedM))); } else { final T w = reducedM.negate().add(FastMath.PI); E = reducedM.add(e.multiply(w.multiply(A).divide(w.negate().add(B)).negate().subtract(reducedM).add(FastMath.PI))); } } final T e1 = e.negate().add(1); final boolean noCancellationRisk = (e1.getReal() + E.getReal() * E.getReal() / 6) >= 0.1; // perform two iterations, each consisting of one Halley step and one Newton-Raphson step for (int j = 0; j < 2; ++j) { final T f; T fd; final T fdd = e.multiply(E.sin()); final T fddd = e.multiply(E.cos()); if (noCancellationRisk) { f = (E.subtract(fdd)).subtract(reducedM); fd = fddd.negate().add(1); } else { f = eMeSinE(E).subtract(reducedM); final T s = E.multiply(0.5).sin(); fd = e1.add(e.multiply(s).multiply(s).multiply(2)); } final T dee = f.multiply(fd).divide(f.multiply(fdd).multiply(0.5).subtract(fd.multiply(fd))); // update eccentric anomaly, using expressions that limit underflow problems final T w = fd.add(dee.multiply(fdd.add(dee.multiply(fddd.divide(3)))).multiply(0.5)); fd = fd.add(dee.multiply(fdd.add(dee.multiply(fddd).multiply(0.5)))); E = E.subtract(f.subtract(dee.multiply(fd.subtract(w))).divide(fd)); } // expand the result back to original range E = E.add(M).subtract(reducedM); return E; } /** Accurate computation of E - e sin(E). * <p> * This method is used when E is close to 0 and e close to 1, * i.e. near the perigee of almost parabolic orbits * </p> * @param E eccentric anomaly * @return E - e sin(E) */ private T eMeSinE(final T E) { T x = (e.negate().add(1)).multiply(E.sin()); final T mE2 = E.negate().multiply(E); T term = E; double d = 0; // the inequality test below IS intentional and should NOT be replaced by a check with a small tolerance for (T x0 = zero.add(Double.NaN); x.getReal() != x0.getReal();) { d += 2; term = term.multiply(mE2.divide(d * (d + 1))); x0 = x; x = x.subtract(term); } return x; } /** Computes the hyperbolic eccentric anomaly from the mean anomaly. * <p> * The algorithm used here for solving hyperbolic Kepler equation is * Danby's iterative method (3rd order) with Vallado's initial guess. * </p> * @param M mean anomaly (rad) * @param ecc eccentricity * @return H the hyperbolic eccentric anomaly */ private T meanToHyperbolicEccentric(final T M, final T ecc) { // Resolution of hyperbolic Kepler equation for keplerian parameters // Initial guess T H; if (ecc.getReal() < 1.6) { if ((-FastMath.PI < M.getReal() && M.getReal() < 0.) || M.getReal() > FastMath.PI) { H = M.subtract(ecc); } else { H = M.add(ecc); } } else { if (ecc.getReal() < 3.6 && M.abs().getReal() > FastMath.PI) { H = M.subtract(ecc.copySign(M)); } else { H = M.divide(ecc.subtract(1)); } } // Iterative computation int iter = 0; do { final T f3 = ecc.multiply(H.cosh()); final T f2 = ecc.multiply(H.sinh()); final T f1 = f3.subtract(1); final T f0 = f2.subtract(H).subtract(M); final T f12 = f1.multiply(2); final T d = f0.divide(f12); final T fdf = f1.subtract(d.multiply(f2)); final T ds = f0.divide(fdf); final T shift = f0.divide(fdf.add(ds.multiply(ds.multiply(f3.divide(6))))); H = H.subtract(shift); if ((shift.abs().getReal()) <= 1.0e-12) { return H; } } while (++iter < 50); throw new MathIllegalArgumentException(OrekitMessages.UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY, iter); } /** {@inheritDoc} */ public T getEquinoctialEx() { return e.multiply(pa.add(raan).cos()); } /** {@inheritDoc} */ public T getEquinoctialEy() { return e.multiply((pa.add(raan)).sin()); } /** {@inheritDoc} */ public T getHx() { // Check for equatorial retrograde orbit if (FastMath.abs(i.getReal() - FastMath.PI) < 1.0e-10) { return this.zero.add(Double.NaN); } return raan.cos().multiply(i.divide(2).tan()); } /** {@inheritDoc} */ public T getHy() { // Check for equatorial retrograde orbit if (FastMath.abs(i.getReal() - FastMath.PI) < 1.0e-10) { return this.zero.add(Double.NaN); } return raan.sin().multiply(i.divide(2).tan()); } /** {@inheritDoc} */ public T getLv() { return pa.add(raan).add(v); } /** {@inheritDoc} */ public T getLE() { return pa.add(raan).add(getEccentricAnomaly()); } /** {@inheritDoc} */ public T getLM() { return pa.add(raan).add(getMeanAnomaly()); } /** {@inheritDoc} */ protected TimeStampedFieldPVCoordinates<T> initFieldPVCoordinates() { // preliminary variables final T cosRaan = raan.cos(); final T sinRaan = raan.sin(); final T cosPa = pa.cos(); final T sinPa = pa.sin(); final T cosI = i.cos(); final T sinI = i.sin(); final T crcp = cosRaan.multiply(cosPa); final T crsp = cosRaan.multiply(sinPa); final T srcp = sinRaan.multiply(cosPa); final T srsp = sinRaan.multiply(sinPa); // reference axes defining the orbital plane final FieldVector3D<T> p = new FieldVector3D<T>( crcp.subtract(cosI.multiply(srsp)), srcp.add(cosI.multiply(crsp)), sinI.multiply(sinPa)); final FieldVector3D<T> q = new FieldVector3D<T>( crsp.add(cosI.multiply(srcp)).negate(), cosI.multiply(crcp).subtract(srsp), sinI.multiply(cosPa)); return (a.getReal() > 0) ? initFieldPVCoordinatesElliptical(p, q) : initFieldPVCoordinatesHyperbolic(p, q); } /** Initialize the position/velocity coordinates, elliptic case. * @param p unit vector in the orbital plane pointing towards perigee * @param q unit vector in the orbital plane in quadrature with p * @return computed position/velocity coordinates */ private TimeStampedFieldPVCoordinates<T> initFieldPVCoordinatesElliptical(final FieldVector3D<T> p, final FieldVector3D<T> q) { // elliptic eccentric anomaly final T uME2 = e.negate().add(1).multiply(e.add(1)); final T s1Me2 = uME2.sqrt(); final T E = getEccentricAnomaly(); final T cosE = E.cos(); final T sinE = E.sin(); // coordinates of position and velocity in the orbital plane final T x = a.multiply(cosE.subtract(e)); final T y = a.multiply(sinE).multiply(s1Me2); final T factor = (a.reciprocal().multiply(getMu())).sqrt().divide(e.negate().multiply(cosE).add(1)); final T xDot = sinE.negate().multiply(factor); final T yDot = cosE.multiply(s1Me2).multiply(factor); final FieldVector3D<T> position = new FieldVector3D<T>(x, p, y, q); final T r2 = x.multiply(x).add(y.multiply(y)); final FieldVector3D<T> velocity = new FieldVector3D<T>(xDot, p, yDot, q); final FieldVector3D<T> acceleration = new FieldVector3D<T>(r2.multiply(r2.sqrt()).reciprocal().multiply(-getMu()), position); return new TimeStampedFieldPVCoordinates<T>(getDate(), position, velocity, acceleration); } /** Initialize the position/velocity coordinates, hyperbolic case. * @param p unit vector in the orbital plane pointing towards perigee * @param q unit vector in the orbital plane in quadrature with p * @return computed position/velocity coordinates */ private TimeStampedFieldPVCoordinates<T> initFieldPVCoordinatesHyperbolic(final FieldVector3D<T> p, final FieldVector3D<T> q) { // compute position and velocity factors final T sinV = v.sin(); final T cosV = v.cos(); final T f = a.multiply(e.multiply(e).negate().add(1)); final T posFactor = f.divide(e.multiply(cosV).add(1)); final T velFactor = f.reciprocal().multiply(getMu()).sqrt(); final FieldVector3D<T> position = new FieldVector3D<T>( posFactor.multiply(cosV), p, posFactor.multiply(sinV), q); final FieldVector3D<T> velocity = new FieldVector3D<T>( velFactor.multiply(sinV).negate(), p, velFactor.multiply(e.add(cosV)), q); final FieldVector3D<T> acceleration = new FieldVector3D<T>(posFactor.multiply(posFactor).multiply(posFactor).reciprocal().multiply(-getMu()), position); return new TimeStampedFieldPVCoordinates<T>(getDate(), position, velocity, acceleration); } /** {@inheritDoc} */ public FieldKeplerianOrbit<T> shiftedBy(final T dt) { return new FieldKeplerianOrbit<T>(a, e, i, pa, raan, getKeplerianMeanMotion().multiply(dt).add(getMeanAnomaly()), PositionAngle.MEAN, getFrame(), getDate().shiftedBy(dt), getMu()); } /** {@inheritDoc} * <p> * The interpolated instance is created by polynomial Hermite interpolation * on Keplerian elements, without derivatives (which means the interpolation * falls back to Lagrange interpolation only). * </p> * <p> * As this implementation of interpolation is polynomial, it should be used only * with small samples (about 10-20 points) in order to avoid <a * href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a> * and numerical problems (including NaN appearing). * </p> * <p> * If orbit interpolation on large samples is needed, using the {@link * org.orekit.propagation.analytical.Ephemeris} class is a better way than using this * low-level interpolation. The Ephemeris class automatically handles selection of * a neighboring sub-sample with a predefined number of point from a large global sample * in a thread-safe way. * </p> */ public FieldKeplerianOrbit<T> interpolate(final FieldAbsoluteDate<T> date, final Collection<FieldOrbit<T>> sample) { // set up an interpolator final FieldHermiteInterpolator<T> interpolator = new FieldHermiteInterpolator<T>(); // add sample points FieldAbsoluteDate<T> previousDate = null; T previousPA = this.zero.add(Double.NaN); T previousRAAN = this.zero.add(Double.NaN); T previousM = this.zero.add(Double.NaN); for (final FieldOrbit<T> orbit : sample) { final FieldKeplerianOrbit<T> kep = new FieldKeplerianOrbit <T>(orbit); // final FieldKeplerianOrbit<T> kep = (FieldKeplerianOrbit) OrbitType.KEPLERIAN.convertType(orbit); final T continuousPA; final T continuousRAAN; final T continuousM; if (previousDate == null) { continuousPA = kep.getPerigeeArgument(); continuousRAAN = kep.getRightAscensionOfAscendingNode(); continuousM = kep.getMeanAnomaly(); } else { final T dt = kep.getDate().durationFrom(previousDate); final T keplerM = previousM.add(kep.getKeplerianMeanMotion().multiply(dt)); continuousPA = normalizeAngle(kep.getPerigeeArgument(), previousPA); continuousRAAN = normalizeAngle(kep.getRightAscensionOfAscendingNode(), previousRAAN); continuousM = normalizeAngle(kep.getMeanAnomaly(), keplerM); } previousDate = kep.getDate(); previousPA = continuousPA; previousRAAN = continuousRAAN; previousM = continuousM; final T[] useless = MathArrays.buildArray(getA().getField(), 6); useless[0] = kep.getA(); useless[1] = kep.getE(); useless[2] = kep.getI(); useless[3] = continuousPA; useless[4] = continuousRAAN; useless[5] = continuousM; //@SuppressWarnings("unchecked") interpolator.addSamplePoint(this.zero.add(kep.getDate().durationFrom(date)), useless); } // interpolate //ADDED RISK CASTING TO T() (it should work i think) final T[] interpolated = (T[]) interpolator.value(this.zero); // build a new interpolated instance return new FieldKeplerianOrbit<T>(interpolated[0], interpolated[1], interpolated[2], interpolated[3], interpolated[4], interpolated[5], PositionAngle.MEAN, getFrame(), date, getMu()); } /** {@inheritDoc} */ protected T[][] computeJacobianMeanWrtCartesian() { if (a.getReal() > 0) { return computeJacobianMeanWrtCartesianElliptical(); } else { return computeJacobianMeanWrtCartesianHyperbolic(); } } /** Compute the Jacobian of the orbital parameters with respect to the cartesian parameters. * <p> * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with * respect to cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3, * yDot for j=4, zDot for j=5). * </p> * @return 6x6 Jacobian matrix */ private T[][] computeJacobianMeanWrtCartesianElliptical() { final T[][] jacobian = MathArrays.buildArray(getA().getField(), 6, 6); // compute various intermediate parameters final FieldPVCoordinates<T> pvc = getPVCoordinates(); final FieldVector3D<T> position = pvc.getPosition(); final FieldVector3D<T> velocity = pvc.getVelocity(); final FieldVector3D<T> momentum = pvc.getMomentum(); final T v2 = velocity.getNormSq(); final T r2 = position.getNormSq(); final T r = r2.sqrt(); final T r3 = r.multiply(r2); final T px = position.getX(); final T py = position.getY(); final T pz = position.getZ(); final T vx = velocity.getX(); final T vy = velocity.getY(); final T vz = velocity.getZ(); final T mx = momentum.getX(); final T my = momentum.getY(); final T mz = momentum.getZ(); final double mu = getMu(); final T sqrtMuA = a.multiply(mu).sqrt(); final T sqrtAoMu = a.divide(mu).sqrt(); final T a2 = a.multiply(a); final T twoA = a.multiply(2); final T rOnA = r.divide(a); final T oMe2 = e.multiply(e).negate().add(1); final T epsilon = oMe2.sqrt(); final T sqrtRec = epsilon.reciprocal(); final T cosI = i.cos(); final T sinI = i.sin(); final T cosPA = pa.cos(); final T sinPA = pa.sin(); final T pv = FieldVector3D.dotProduct(position, velocity); final T cosE = a.subtract(r).divide(a.multiply(e)); final T sinE = pv.divide(e.multiply(sqrtMuA)); // da final FieldVector3D<T> vectorAR = new FieldVector3D<T>(a2.multiply(2).divide(r3), position); final FieldVector3D<T> vectorARDot = velocity.scalarMultiply(a2.multiply(2 / mu)); fillHalfRow(this.one, vectorAR, jacobian[0], 0); fillHalfRow(this.one, vectorARDot, jacobian[0], 3); // de final T factorER3 = pv.divide(twoA); final FieldVector3D<T> vectorER = new FieldVector3D<T>(cosE.multiply(v2).divide(r.multiply(mu)), position, sinE.divide(sqrtMuA), velocity, factorER3.negate().multiply(sinE).divide(sqrtMuA), vectorAR); final FieldVector3D<T> vectorERDot = new FieldVector3D<T>(sinE.divide(sqrtMuA), position, cosE.multiply(2 / mu).multiply(r), velocity, factorER3.negate().multiply(sinE).divide(sqrtMuA), vectorARDot); fillHalfRow(this.one, vectorER, jacobian[1], 0); fillHalfRow(this.one, vectorERDot, jacobian[1], 3); // dE / dr (Eccentric anomaly) final T coefE = cosE.divide(e.multiply(sqrtMuA)); final FieldVector3D<T> vectorEAnR = new FieldVector3D<T>(sinE.negate().multiply(v2).divide(e.multiply(r).multiply(mu)), position, coefE, velocity, factorER3.negate().multiply(coefE), vectorAR); // dE / drDot final FieldVector3D<T> vectorEAnRDot = new FieldVector3D<T>(sinE.multiply(-2).multiply(r).divide(e.multiply(mu)), velocity, coefE, position, factorER3.negate().multiply(coefE), vectorARDot); // precomputing some more factors final T s1 = sinE.negate().multiply(pz).divide(r).subtract(cosE.multiply(vz).multiply(sqrtAoMu)); final T s2 = cosE.negate().multiply(pz).divide(r3); final T s3 = sinE.multiply(vz).divide(sqrtMuA.multiply(-2)); final T t1 = sqrtRec.multiply(cosE.multiply(pz).divide(r).subtract(sinE.multiply(vz).multiply(sqrtAoMu))); final T t2 = sqrtRec.multiply(sinE.negate().multiply(pz).divide(r3)); final T t3 = sqrtRec.multiply(cosE.subtract(e)).multiply(vz).divide(sqrtMuA.multiply(2)); final T t4 = sqrtRec.multiply(e.multiply(sinI).multiply(cosPA).multiply(sqrtRec).subtract(vz.multiply(sqrtAoMu))); final FieldVector3D<T> s = new FieldVector3D<T>(cosE.divide(r), this.PLUS_K, s1, vectorEAnR, s2, position, s3, vectorAR); final FieldVector3D<T> sDot = new FieldVector3D<T>( sinE.negate().multiply(sqrtAoMu), this.PLUS_K, s1, vectorEAnRDot, s3, vectorARDot); final FieldVector3D<T> t = new FieldVector3D<T>(sqrtRec.multiply(sinE).divide(r), this.PLUS_K).add(new FieldVector3D<T>(t1, vectorEAnR, t2, position, t3, vectorAR, t4, vectorER)); final FieldVector3D<T> tDot = new FieldVector3D<T>(sqrtRec.multiply(cosE.subtract(e)).multiply(sqrtAoMu), this.PLUS_K, t1, vectorEAnRDot, t3, vectorARDot, t4, vectorERDot); // di final T factorI1 = sinI.negate().multiply(sqrtRec).divide(sqrtMuA); final T i1 = factorI1; final T i2 = factorI1.negate().multiply(mz).divide(twoA); final T i3 = factorI1.multiply(mz).multiply(e).divide(oMe2); final T i4 = cosI.multiply(sinPA); final T i5 = cosI.multiply(cosPA); fillHalfRow(i1, new FieldVector3D<T>(vy, vx.negate(), this.zero), i2, vectorAR, i3, vectorER, i4, s, i5, t, jacobian[2], 0); fillHalfRow(i1, new FieldVector3D<T>(py.negate(), px, this.zero), i2, vectorARDot, i3, vectorERDot, i4, sDot, i5, tDot, jacobian[2], 3); // dpa fillHalfRow(cosPA.divide(sinI), s, sinPA.negate().divide(sinI), t, jacobian[3], 0); fillHalfRow(cosPA.divide(sinI), sDot, sinPA.negate().divide(sinI), tDot, jacobian[3], 3); // dRaan final T factorRaanR = (a.multiply(mu).multiply(oMe2).multiply(sinI).multiply(sinI)).reciprocal(); fillHalfRow( factorRaanR.negate().multiply(my), new FieldVector3D<T>(zero, vz, vy.negate()), factorRaanR.multiply(mx), new FieldVector3D<T>(vz.negate(), zero, vx), jacobian[4], 0); fillHalfRow(factorRaanR.negate().multiply(my), new FieldVector3D<T>(zero, pz.negate(), py), factorRaanR.multiply(mx), new FieldVector3D<T>(pz, zero, px.negate()), jacobian[4], 3); // dM fillHalfRow(rOnA, vectorEAnR, sinE.negate(), vectorER, jacobian[5], 0); fillHalfRow(rOnA, vectorEAnRDot, sinE.negate(), vectorERDot, jacobian[5], 3); return jacobian; } /** Compute the Jacobian of the orbital parameters with respect to the cartesian parameters. * <p> * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with * respect to cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3, * yDot for j=4, zDot for j=5). * </p> * @return 6x6 Jacobian matrix */ private T[][] computeJacobianMeanWrtCartesianHyperbolic() { final T[][] jacobian = MathArrays.buildArray(getA().getField(), 6, 6); // compute various intermediate parameters final FieldPVCoordinates<T> pvc = getPVCoordinates(); final FieldVector3D<T> position = pvc.getPosition(); final FieldVector3D<T> velocity = pvc.getVelocity(); final FieldVector3D<T> momentum = pvc.getMomentum(); final T r2 = position.getNormSq(); final T r = r2.sqrt(); final T r3 = r.multiply(r2); final T x = position.getX(); final T y = position.getY(); final T z = position.getZ(); final T vx = velocity.getX(); final T vy = velocity.getY(); final T vz = velocity.getZ(); final T mx = momentum.getX(); final T my = momentum.getY(); final T mz = momentum.getZ(); final double mu = getMu(); final T absA = a.negate(); final T sqrtMuA = absA.multiply(mu).sqrt(); final T a2 = a.multiply(a); final T rOa = r.divide(absA); final T cosI = i.cos(); final T sinI = i.sin(); final T pv = FieldVector3D.dotProduct(position, velocity); // da final FieldVector3D<T> vectorAR = new FieldVector3D<T>(a2.multiply(-2).divide(r3), position); final FieldVector3D<T> vectorARDot = velocity.scalarMultiply(a2.multiply(-2 / mu)); fillHalfRow(this.one.negate(), vectorAR, jacobian[0], 0); fillHalfRow(this.one.negate(), vectorARDot, jacobian[0], 3); // differentials of the momentum final T m = momentum.getNorm(); final T oOm = m.reciprocal(); final FieldVector3D<T> dcXP = new FieldVector3D<T>(this.zero, vz, vy.negate()); final FieldVector3D<T> dcYP = new FieldVector3D<T>(vz.negate(), this.zero, vx); final FieldVector3D<T> dcZP = new FieldVector3D<T>( vy, vx.negate(), this.zero); final FieldVector3D<T> dcXV = new FieldVector3D<T>( this.zero, z.negate(), y); final FieldVector3D<T> dcYV = new FieldVector3D<T>( z, this.zero, x.negate()); final FieldVector3D<T> dcZV = new FieldVector3D<T>( y.negate(), x, this.zero); final FieldVector3D<T> dCP = new FieldVector3D<T>(mx.multiply(oOm), dcXP, my.multiply(oOm), dcYP, mz.multiply(oOm), dcZP); final FieldVector3D<T> dCV = new FieldVector3D<T>(mx.multiply(oOm), dcXV, my.multiply(oOm), dcYV, mz.multiply(oOm), dcZV); // dp final T mOMu = m.divide(mu); final FieldVector3D<T> dpP = new FieldVector3D<T>(mOMu.multiply(2), dCP); final FieldVector3D<T> dpV = new FieldVector3D<T>(mOMu.multiply(2), dCV); // de final T p = m.multiply(mOMu); final T moO2ae = absA.multiply(2).multiply(e).reciprocal(); final T m2OaMu = p.negate().divide(absA); fillHalfRow(moO2ae, dpP, m2OaMu.multiply(moO2ae), vectorAR, jacobian[1], 0); fillHalfRow(moO2ae, dpV, m2OaMu.multiply(moO2ae), vectorARDot, jacobian[1], 3); // di final T cI1 = m.multiply(sinI).reciprocal(); final T cI2 = cosI.multiply(cI1); fillHalfRow(cI2, dCP, cI1.negate(), dcZP, jacobian[2], 0); fillHalfRow(cI2, dCV, cI1.negate(), dcZV, jacobian[2], 3); // dPA final T cP1 = y.multiply(oOm); final T cP2 = x.negate().multiply(oOm); final T cP3 = mx.multiply(cP1).add(my.multiply(cP2)).negate(); final T cP4 = cP3.multiply(oOm); final T cP5 = r2.multiply(sinI).multiply(sinI).negate().reciprocal(); final T cP6 = z.multiply(cP5); final T cP7 = cP3.multiply(cP5); final FieldVector3D<T> dacP = new FieldVector3D<T>(cP1, dcXP, cP2, dcYP, cP4, dCP, oOm, new FieldVector3D<T>(my.negate(), mx, this.zero)); final FieldVector3D<T> dacV = new FieldVector3D<T>(cP1, dcXV, cP2, dcYV, cP4, dCV); final FieldVector3D<T> dpoP = new FieldVector3D<T>(cP6, dacP, cP7, this.PLUS_K); final FieldVector3D<T> dpoV = new FieldVector3D<T>(cP6, dacV); final T re2 = r2.multiply(e).multiply(e); final T recOre2 = p.subtract(r).divide(re2); final T resOre2 = pv.multiply(mOMu).divide(re2); final FieldVector3D<T> dreP = new FieldVector3D<T>(mOMu, velocity, pv.divide(mu), dCP); final FieldVector3D<T> dreV = new FieldVector3D<T>(mOMu, position, pv.divide(mu), dCV); final FieldVector3D<T> davP = new FieldVector3D<T>(resOre2.negate(), dpP, recOre2, dreP, resOre2.divide(r), position); final FieldVector3D<T> davV = new FieldVector3D<T>(resOre2.negate(), dpV, recOre2, dreV); fillHalfRow(this.one, dpoP, this.one.negate(), davP, jacobian[3], 0); fillHalfRow(this.one, dpoV, this.one.negate(), davV, jacobian[3], 3); // dRAAN final T cO0 = cI1.multiply(cI1); final T cO1 = mx.multiply(cO0); final T cO2 = my.negate().multiply(cO0); fillHalfRow(cO1, dcYP, cO2, dcXP, jacobian[4], 0); fillHalfRow(cO1, dcYV, cO2, dcXV, jacobian[4], 3); // dM final T s2a = pv.divide(absA.multiply(2)); final T oObux = m.multiply(m).add(absA.multiply(mu)).sqrt().reciprocal(); final T scasbu = pv.multiply(oObux); final FieldVector3D<T> dauP = new FieldVector3D<T>(sqrtMuA.reciprocal(), velocity, s2a.negate().divide(sqrtMuA), vectorAR); final FieldVector3D<T> dauV = new FieldVector3D<T>(sqrtMuA.reciprocal(), position, s2a.negate().divide(sqrtMuA), vectorARDot); final FieldVector3D<T> dbuP = new FieldVector3D<T>(oObux.multiply(mu / 2), vectorAR, m.multiply(oObux), dCP); final FieldVector3D<T> dbuV = new FieldVector3D<T>(oObux.multiply(mu / 2), vectorARDot, m.multiply(oObux), dCV); final FieldVector3D<T> dcuP = new FieldVector3D<T>(oObux, velocity, scasbu.negate().multiply(oObux), dbuP); final FieldVector3D<T> dcuV = new FieldVector3D<T>(oObux, position, scasbu.negate().multiply(oObux), dbuV); fillHalfRow(this.one, dauP, e.negate().divide(rOa.add(1)), dcuP, jacobian[5], 0); fillHalfRow(this.one, dauV, e.negate().divide(rOa.add(1)), dcuV, jacobian[5], 3); return jacobian; } /** {@inheritDoc} */ protected T[][] computeJacobianEccentricWrtCartesian() { if (a.getReal() > 0) { return computeJacobianEccentricWrtCartesianElliptical(); } else { return computeJacobianEccentricWrtCartesianHyperbolic(); } } /** Compute the Jacobian of the orbital parameters with respect to the cartesian parameters. * <p> * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with * respect to cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3, * yDot for j=4, zDot for j=5). * </p> * @return 6x6 Jacobian matrix */ private T[][] computeJacobianEccentricWrtCartesianElliptical() { // start by computing the Jacobian with mean angle final T[][] jacobian = computeJacobianMeanWrtCartesianElliptical(); // Differentiating the Kepler equation M = E - e sin E leads to: // dM = (1 - e cos E) dE - sin E de // which is inverted and rewritten as: // dE = a/r dM + sin E a/r de final T eccentricAnomaly = getEccentricAnomaly(); final T cosE = eccentricAnomaly.cos(); final T sinE = eccentricAnomaly.sin(); final T aOr = e.negate().multiply(cosE).add(1).reciprocal(); // update anomaly row final T[] eRow = jacobian[1]; final T[] anomalyRow = jacobian[5]; for (int j = 0; j < anomalyRow.length; ++j) { anomalyRow[j] = aOr.multiply(anomalyRow[j].add(sinE.multiply(eRow[j]))); } return jacobian; } /** Compute the Jacobian of the orbital parameters with respect to the cartesian parameters. * <p> * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with * respect to cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3, * yDot for j=4, zDot for j=5). * </p> * @return 6x6 Jacobian matrix */ private T[][] computeJacobianEccentricWrtCartesianHyperbolic() { // start by computing the Jacobian with mean angle final T[][] jacobian = computeJacobianMeanWrtCartesianHyperbolic(); // Differentiating the Kepler equation M = e sinh H - H leads to: // dM = (e cosh H - 1) dH + sinh H de // which is inverted and rewritten as: // dH = 1 / (e cosh H - 1) dM - sinh H / (e cosh H - 1) de final T H = getEccentricAnomaly(); final T coshH = H.cosh(); final T sinhH = H.sinh(); final T absaOr = e.multiply(coshH).subtract(1).reciprocal(); // update anomaly row final T[] eRow = jacobian[1]; final T[] anomalyRow = jacobian[5]; for (int j = 0; j < anomalyRow.length; ++j) { anomalyRow[j] = absaOr.multiply(anomalyRow[j].subtract(sinhH.multiply(eRow[j]))); } return jacobian; } /** {@inheritDoc} */ protected T[][] computeJacobianTrueWrtCartesian() { if (a.getReal() > 0) { return computeJacobianTrueWrtCartesianElliptical(); } else { return computeJacobianTrueWrtCartesianHyperbolic(); } } /** Compute the Jacobian of the orbital parameters with respect to the cartesian parameters. * <p> * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with * respect to cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3, * yDot for j=4, zDot for j=5). * </p> * @return 6x6 Jacobian matrix */ private T[][] computeJacobianTrueWrtCartesianElliptical() { // start by computing the Jacobian with eccentric angle final T[][] jacobian = computeJacobianEccentricWrtCartesianElliptical(); // Differentiating the eccentric anomaly equation sin E = sqrt(1-e^2) sin v / (1 + e cos v) // and using cos E = (e + cos v) / (1 + e cos v) to get rid of cos E leads to: // dE = [sqrt (1 - e^2) / (1 + e cos v)] dv - [sin E / (1 - e^2)] de // which is inverted and rewritten as: // dv = sqrt (1 - e^2) a/r dE + [sin E / sqrt (1 - e^2)] a/r de final T e2 = e.multiply(e); final T oMe2 = e2.negate().add(1); final T epsilon = oMe2.sqrt(); final T eccentricAnomaly = getEccentricAnomaly(); final T cosE = eccentricAnomaly.cos(); final T sinE = eccentricAnomaly.sin(); final T aOr = e.multiply(cosE).negate().add(1).reciprocal(); final T aFactor = epsilon.multiply(aOr); final T eFactor = sinE.multiply(aOr).divide(epsilon); // update anomaly row final T[] eRow = jacobian[1]; final T[] anomalyRow = jacobian[5]; for (int j = 0; j < anomalyRow.length; ++j) { anomalyRow[j] = aFactor.multiply(anomalyRow[j]).add(eFactor.multiply(eRow[j])); } return jacobian; } /** Compute the Jacobian of the orbital parameters with respect to the cartesian parameters. * <p> * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with * respect to cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3, * yDot for j=4, zDot for j=5). * </p> * @return 6x6 Jacobian matrix */ private T[][] computeJacobianTrueWrtCartesianHyperbolic() { // start by computing the Jacobian with eccentric angle final T[][] jacobian = computeJacobianEccentricWrtCartesianHyperbolic(); // Differentiating the eccentric anomaly equation sinh H = sqrt(e^2-1) sin v / (1 + e cos v) // and using cosh H = (e + cos v) / (1 + e cos v) to get rid of cosh H leads to: // dH = [sqrt (e^2 - 1) / (1 + e cos v)] dv + [sinh H / (e^2 - 1)] de // which is inverted and rewritten as: // dv = sqrt (1 - e^2) a/r dH - [sinh H / sqrt (e^2 - 1)] a/r de final T e2 = e.multiply(e); final T e2Mo = e2.subtract(1); final T epsilon = e2Mo.sqrt(); final T H = getEccentricAnomaly(); final T coshH = H.cosh(); final T sinhH = H.sinh(); final T aOr = e.multiply(coshH).subtract(1).reciprocal(); final T aFactor = epsilon.multiply(aOr); final T eFactor = sinhH .multiply(aOr).divide(epsilon); // update anomaly row final T[] eRow = jacobian[1]; final T[] anomalyRow = jacobian[5]; for (int j = 0; j < anomalyRow.length; ++j) { anomalyRow[j] = aFactor.multiply(anomalyRow[j]).subtract(eFactor.multiply(eRow[j])); } return jacobian; } /** {@inheritDoc} */ public void addKeplerContribution(final PositionAngle type, final double gm, final T[] pDot) { final T oMe2; final T ksi; final T absA = a.abs(); final T n = absA.reciprocal().multiply(gm).sqrt().divide(absA); switch (type) { case MEAN : pDot[5] = pDot[5].add(n); break; case ECCENTRIC : oMe2 = e.multiply(e).negate().add(1).abs(); ksi = e.multiply(v.cos()).add(1); pDot[5] = pDot[5].add( n.multiply(ksi).divide(oMe2)); break; case TRUE : oMe2 = e.multiply(e).negate().add(1).abs(); ksi = e.multiply(v.cos()).add(1); pDot[5] = pDot[5].add(n.multiply(ksi).multiply(ksi).divide(oMe2.multiply(oMe2.sqrt()))); break; default : throw new OrekitInternalError(null); } } /** Returns a string representation of this keplerian parameters object. * @return a string representation of this object */ public String toString() { return new StringBuffer().append("keplerian parameters: ").append('{'). append("a: ").append(a.getReal()). append("; e: ").append(e.getReal()). append("; i: ").append(FastMath.toDegrees(i.getReal())). append("; pa: ").append(FastMath.toDegrees(pa.getReal())). append("; raan: ").append(FastMath.toDegrees(raan.getReal())). append("; v: ").append(FastMath.toDegrees(v.getReal())). append(";}").toString(); } // // /** Replace the instance with a data transfer object for serialization. // * @return data transfer object that will be serialized // */ // private Object writeReplace() { // return new DTO(this); // } // /** Internal class used only for serialization. */ // private class DTO implements Serializable { // // /** Serializable UID. */ // private static final long serialVersionUID = 20140617L; // // /** Double values. */ // private T[] d; // // /** Frame in which are defined the orbital parameters. */ // private final Frame frame; // // /** Simple constructor. // * @param orbit instance to serialize // */ // private DTO(final FieldKeplerianOrbit<T> orbit) { // // final TimeStampedFieldPVCoordinates<T> pv = orbit.getFieldPVCoordinates(); // final FieldAbsoluteDate<T> FAD = new FieldAbsoluteDate<T>(orbit.a.getField()); // // decompose date // final double epoch = FastMath.floor(pv.getDate().durationFrom(FAD.J2000_EPOCH(orbit.a.getField())).getReal()); // final T offset = pv.getDate().durationFrom(FAD.J2000_EPOCH(orbit.a.getField()).shiftedBy(epoch)); // // this.d = MathArrays.buildArray(getA().getField(), 9); // this.d[0] = a.getField().getZero().add(epoch); // this.d[1] = a.getField().getZero().add(offset); // this.d[2] = a.getField().getZero().add(orbit.getMu()); // this.d[3] = orbit.a; // this.d[4] = orbit.e; // this.d[5] = orbit.i; // this.d[6] = orbit.pa; // this.d[7] = orbit.raan; // this.d[8] = orbit.v; // // this.frame = orbit.getFrame(); // // } // // /** Replace the deserialized data transfer object with a {@link FieldKeplerianOrbit}. // * @return replacement {@link FieldKeplerianOrbit} // */ // /** // private Object readResolve() { // return new FieldKeplerianOrbit<T>(d[3], d[4], d[5], d[6], d[7], d[8], PositionAngle.TRUE, // frame, FieldAbsoluteDate<T>.J2000_EPOCH.shiftedBy(d[0]).shiftedBy(d[1]), // d[2]); // } // */ // } /** * Normalize an angle in a 2π wide interval around a center value. * <p>This method has three main uses:</p> * <ul> * <li>normalize an angle between 0 and 2π:<br/> * {@code a = MathUtils.normalizeAngle(a, FastMath.PI);}</li> * <li>normalize an angle between -π and +π<br/> * {@code a = MathUtils.normalizeAngle(a, 0.0);}</li> * <li>compute the angle between two defining angular positions:<br> * {@code angle = MathUtils.normalizeAngle(end, start) - start;}</li> * </ul> * <p>Note that due to numerical accuracy and since π cannot be represented * exactly, the result interval is <em>closed</em>, it cannot be half-closed * as would be more satisfactory in a purely mathematical view.</p> * @param a angle to normalize * @param center center of the desired 2π interval for the result * @param <T> the type of the field elements * @return a-2kπ with integer k and center-π <= a-2kπ <= center+π * @since 1.2 */ public static <T extends RealFieldElement<T>> T normalizeAngle(final T a, final T center) { return a.subtract(2 * FastMath.PI * FastMath.floor((a.getReal() + FastMath.PI - center.getReal()) / (2 * FastMath.PI))); } @Override public KeplerianOrbit toOrbit() { return new KeplerianOrbit(a.getReal(), e.getReal(), i.getReal(), pa.getReal(), raan.getReal(), v.getReal(), PositionAngle.TRUE, getFrame(), getDate().toAbsoluteDate(), getMu()); } }